Skip to content

Commit 8ec1c6d

Browse files
committed
feat(api): ✨ Add x-forwarded-for and x-forwarded-proto support
1 parent b35e54c commit 8ec1c6d

4 files changed

Lines changed: 36 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
- [x] 🥺 Emoji Reactions are now available! You can react to any note with custom emojis.
88
- [x] 🔎 Added support for [batch account data API](https://docs.joinmastodon.org/methods/accounts/#index).
9+
- [x] 🛡️ Added support for `X-Forwarded-For` and `X-Forwarded-Proto` headers from trusted proxies.
910

1011
### Backend
1112

config/config.example.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ base_url = "https://example.com"
6565
bind = "0.0.0.0"
6666
bind_port = 8080
6767

68+
# IPs of trusted proxies (for X-Forwarded-For and X-Forwarded-Proto headers)
69+
# v4, v6, ranges and wildcards are supported
70+
# Your proxy must set these headers correctly for versia-server to work
71+
proxy_ips = []
72+
6873
# Bans IPv4 or IPv6 IPs (wildcards, networks and ranges are supported)
6974
banned_ips = []
7075
# Banned user agents, regex format

packages/config/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@ export const ConfigSchema = z
384384
),
385385
bind: z.string().min(1).default("0.0.0.0"),
386386
bind_port: unixPort.default(8080),
387+
proxy_ips: z.array(ip).default([]),
387388
banned_ips: z.array(ip).default([]),
388389
banned_user_agents: z.array(regex).default([]),
389390
proxy_address: url

utils/server.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { ConfigSchema } from "@versia-server/config";
22
import { debugResponse } from "@versia-server/kit/api";
33
import { type Server, serve } from "bun";
44
import type { Hono } from "hono";
5+
import { matches } from "ip-matching";
56
import type { z } from "zod";
67
import type { HonoEnv } from "~/types/api.ts";
78

@@ -22,7 +23,34 @@ export const createServer = (
2223
: undefined,
2324
hostname: config.http.bind,
2425
async fetch(req, server): Promise<Response> {
25-
const output = await app.fetch(req, { ip: server.requestIP(req) });
26+
const ip = server.requestIP(req);
27+
const isTrustedProxy =
28+
config.http.proxy_ips.includes("*") ||
29+
(ip &&
30+
config.http.proxy_ips.some((proxyIp) =>
31+
matches(ip.address, proxyIp),
32+
));
33+
34+
const url = new URL(req.url);
35+
36+
if (ip && isTrustedProxy) {
37+
const xff = req.headers.get("x-forwarded-for");
38+
const xfp = req.headers.get("x-forwarded-proto");
39+
40+
if (xff) {
41+
const forwardedIps = xff.split(",").map((s) => s.trim());
42+
const originalIp = forwardedIps[0];
43+
44+
ip.address = originalIp;
45+
ip.family = originalIp.includes(":") ? "IPv6" : "IPv4";
46+
}
47+
48+
if (xfp) {
49+
url.protocol = xfp;
50+
}
51+
}
52+
53+
const output = await app.fetch(req, { ip });
2654

2755
await debugResponse(output.clone());
2856

0 commit comments

Comments
 (0)