Skip to content

Commit 641663c

Browse files
committed
Add post
1 parent 93663d7 commit 641663c

7 files changed

Lines changed: 104 additions & 2 deletions

File tree

.github/copilot-instructions.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,10 @@ This is my personal website with posts and notes.
2929
- Do not use self-closing tags for Svelte components and HTML elements.
3030
- Use TailwindCSS for styling and tailwind-merge for class merging.
3131
- Comments use proper punctuation and end with a period.
32+
33+
## Writing style
34+
35+
- Use American English.
36+
- Be concise and clear in your explanations.
37+
- Use active voice and present tense.
38+
- Write a tad informal, but not too casual.

src/routes/api/tags/[id]/tags.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const tags: TagType[] = [
4242
label: 'Gitpod',
4343
path: '/tags/gitpod'
4444
},
45+
{ id: 'railway', label: 'Railway', path: '/tags/railway' },
4546
{
4647
id: 'screen-recording',
4748
label: 'Screen recording',

src/routes/posts/(2024)/github-copilot-context/meta.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { PostMetaType } from '@maiertech/sveltekit-helpers';
22

3-
const postGithubCopilotContext: PostMetaType = {
3+
const meta: PostMetaType = {
44
title: 'How to provide better context in GitHub Copilot prompts',
55
author: 'thilo',
66
publishedDate: '2024-11-15',
@@ -11,4 +11,4 @@ const postGithubCopilotContext: PostMetaType = {
1111
filepath: 'src/routes/posts/(2024)/github-copilot-context/+page.svx'
1212
};
1313

14-
export default postGithubCopilotContext;
14+
export default meta;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import type { PageServerLoad } from './$types';
2+
import { transformPostWithLastmodDate } from '$lib/server/transformations';
3+
import meta from './meta';
4+
5+
export const load: PageServerLoad = async (event) => {
6+
const post = await transformPostWithLastmodDate(meta, event);
7+
const { title, description } = post;
8+
9+
return { post, seo: { title, description } };
10+
};
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<script>
2+
import { Figure, ResponsiveImage } from '@maiertech/sveltekit-helpers';
3+
import srcProbingBots from './probing-bots.png';
4+
</script>
5+
6+
After migrating this website to [Railway](https://railway.com/), I noticed bots probing for
7+
accidentally exposed vulnerable files:
8+
9+
<Figure caption="HTTP Logs from Railway after first deployment with a custom domain." class="mb-8">
10+
<ResponsiveImage src={srcProbingBots} alt="Log entries on Railway.com showing bot requests to
11+
potentially exposed files, for example, `/.env`." intrinsicWidth={1032} aspectRatio={16/9}></ResponsiveImage>
12+
</Figure>
13+
14+
Since my website is built with SvelteKit, it returns a 404 for these types of requests. Nothing to
15+
worry about in terms of security. But all these 404 responses are processed by SvelteKit and consume
16+
resources on the server. This is especially annoying because Railway's pricing model is based on the
17+
resources a deployment consumes.
18+
19+
The obvious solution is to host the SvelteKit app behind a web application firewall (WAF) that
20+
blocks these types of requests before they reach the server. Unfortunately, Railway does not
21+
currently offer a WAF. So, I thought, why not let SvelteKit play WAF and make it drop these
22+
requests?
23+
24+
Here is what I came up with:
25+
26+
<Figure caption="hooks.server.ts" class="mb-8">
27+
28+
```ts
29+
import { type Handle } from '@sveltejs/kit';
30+
import { Blocklist } from '$lib/utils/index.js';
31+
import { BLOCKED_PATHS } from '$lib/blocklists/index.js';
32+
33+
const pathBlocklist = new Blocklist(BLOCKED_PATHS);
34+
35+
export const handle: Handle = async ({ event, resolve }) => {
36+
const { url } = event;
37+
38+
if (pathBlocklist.isBlocked(url.pathname)) {
39+
return new Response(null, { status: 204 });
40+
}
41+
42+
return resolve(event);
43+
};
44+
```
45+
46+
</Figure>
47+
48+
Inside the `handle` hook in `hooks.server.ts`, I check if the request path is on a blocklist. The
49+
blocklist is a
50+
[`Set`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) that
51+
contains paths used by bots from my Railway logs. Since SvelteKit handles requests at the
52+
application layer, it always wants to return a response. Even if I return `undefined` after
53+
detecting a malicious request, SvelteKit still returns a 500 server error.
54+
55+
A 500 server error probably consumes the same amount of resources as the original 404 response. So,
56+
I don't gain anything with this approach. The 204 no content response in the code above might shave
57+
off a little bit of processing compared to a 404 or 500 status. But it still returns a response,
58+
which also messes up my Railway logs because 204 responses show up as successful requests.
59+
60+
Lesson learned: **SvelteKit cannot drop requests at the application layer.**
61+
62+
So, what did I do instead? I proxied the SvelteKit app through
63+
[Cloudflare](https://www.cloudflare.com/). Its firewall and bot detection take care of malicious
64+
requests and make sure they never reach the SvelteKit app hosted on Railway. Not exactly an elegant
65+
solution, but it works.
66+
67+
Cloudflare and [Vercel](https://vercel.com/) have invested a lot into their WAFs lately, and if you
68+
have ever checked your WAF logs, you might have been stunned by how much garbage they block. I hope
69+
Railway (and other boutique hosters) will also offer a basic WAF in the not-too-distant future.
70+
After all, I really want the non-big-tech hosting competition to succeed and be a viable option.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import type { PostMetaType } from '@maiertech/sveltekit-helpers';
2+
3+
const meta: PostMetaType = {
4+
title: 'Dropping requests in SvelteKit',
5+
author: 'thilo',
6+
publishedDate: '2025-07-01',
7+
description:
8+
"SvelteKit can't drop bad requests at the app layer, so I use Cloudflare's WAF to block bots before they hit my Railway-hosted site.",
9+
tags: ['svelte', 'railway'],
10+
path: '/posts/dropping-requests-in-sveltekit',
11+
filepath: 'src/routes/posts/(2025)/dropping-requests-in-sveltekit/+page.svx'
12+
};
13+
14+
export default meta;
166 KB
Loading

0 commit comments

Comments
 (0)