I tried adding this to my express middleware to avoid this issue:
const primaryHost = 'kentcdodds.com'
const getHost = (req: {get: (key: string) => string | undefined}) =>
req.get('X-Forwarded-Host') ?? req.get('host') ?? ''
if (process.env.FLY) {
app.use((req, res, next) => {
const host = getHost(req)
const allowedHosts = [primaryHost, 'kcd.fly.dev', 'kcd-staging.fly.dev']
// TODO: figure out if we can determine the IP address that fly uses for the healthcheck
const isIPAddress = /\d+\.\d+\.\d+\.\d+/.test(host)
if (allowedHosts.some(h => host.endsWith(h)) || isIPAddress) {
return next()
} else {
console.log(`đș disallowed host redirected: ${host}${req.originalUrl}`)
return res.redirect(`https://${primaryHost}${req.originalUrl}`)
}
})
}
However, it appears whoeverâs proxying has tricked us into getting kentcdodds.com in the host header. Iâm not sure where to go from here. Any ideas?
Iâm probably going to add a last-ditch client-side JavaScript thing to redirect users to the proper domain in the browser. But Iâd love to be able to do all this server side.
EDIT: I realized that whoeverâs proxying my site is actually processing responses to find/replace kentcdodds.com with www.butigim.net so any client-side JavaScript thing I do to redirect userâs wonât work. They also can change headers so access-control-allow-origin wonât work either. I need this to work server-side so I donât send requests from their proxy anything useful. Anyone have ideas?
After looking at request headers, I noticed that the proxying site has a fly-client-ip that is not included in the x-forwarded-for header. So I decided that if thatâs detected, weâll just send a nice message:
export const proxyRedirectMiddleware: RequestHandler = (req, res, next) => {
const host = getHost(req)
// TODO: figure out if we can determine the IP address that fly uses for the healthcheck
const isIPAddress = /\d+\.\d+\.\d+\.\d+/.test(host)
if (!allowedHosts.some(h => host.endsWith(h)) && !isIPAddress) {
console.log(`đș disallowed host redirected: ${host}${req.originalUrl}`)
return res.redirect(`https://${primaryHost}${req.originalUrl}`)
}
const flyClientIp = req.get('Fly-Client-IP')
const xForwardedFor = req.get('X-Forwarded-For')
if (!flyClientIp || !xForwardedFor) {
// this should never happen, but just in case...
return next()
}
if (xForwardedFor.includes(flyClientIp)) {
return next()
} else {
// https://fly.io/docs/reference/runtime-environment/#fly-client-ip
// the fly-client-ip header is the IP address of the client that initiated the request
// and if it's not found in the x-forwarded-for header, then we know something fishy is going on đ
console.log(`đș disallowed ip address replied to:`, {
xForwardedFor,
flyClientIp,
})
return res.send(
'Please go to https://kcd.dev instead! Ping Kent if you think you should not be seeing this...',
)
}
}
This seems to work, but Iâm a bit concerned. Am I correct is expecting that the x-forwarded-for should always include the fly-client-ip and if it doesnât then thereâs something fishy going on? Any other ideas on better ways to avoid this?
I believe, using Cloudflare to proxy traffic to other web properties is against their ToS. Surprised this happens, but if you know anyone at Cloudflare, it might be worth a shot at getting the offender shut down.