A guide to implementing Arcjet rate limiting protection in SvelteKit web applications.
In the world of web development, ensuring stability and security is crucial. One of the ways to do so is to utilize rate limiting. In this article, you will learn how to integrate this protective measure into your SvelteKit form actions using Arcjet.
Let’s get started.
Rate limiting is a technique used to control the number of requests or actions allowed in a given period of time to prevent abuse and ensure the fair use of resources. It acts as a line of defense against malicious attackers when they utilize tools to automate sending a large number of requests in a short amount of time.
Attacks that can be thwarted by implementing rate limiting include:
To implement this, we're going to use Arcjet, a developer security SDK designed to protect web applications from various threats. In addition to rate limiting, Arcjet provides tools for bot detection, attack detection, email validation, and more, making it easier to secure your application effectively.
There are many configuration options available in Arcjet, allowing you to fine-tune your rate limiting implementation to best suit your needs and the needs of your users.
The value of the characteristics option defines what the rate limiting is tracked against. The following default options are available:
Characteristic | Description |
---|---|
ip.src |
Identify the client by their IP address. |
http.host |
Identify the client by the requesting host. |
http.request.headers[""] |
Identify the client by the value of a specific request header/list of request headers. |
http.request.cookie[""] |
Identify the client by the value of a specific cookie/list of cookies. |
http.request.uri.args[""] |
Track requests using a certain URL query parameter/list of URL query parameters. |
http.request.uri.path |
Track requests sent by path. |
You can also create custom characteristics and assign them a value of type string, number or boolean when calling the protect
method (see below).
The mode can be one of two values - either LIVE
or DRY RUN
. Using LIVE
will implement the rate limiting protections. DRY RUN
is used for testing purposes - the result will be logged, but requests will not actually be blocked.
The value of the match option is the exact path you want to apply the rate limiting to. If not specified, it will default to the current path that Arcjet is called from or across all paths if Arcjet is running from middleware.
The value of the max option specifies the maximum number of requests allowed within a certain time frame.
The value of the window option specifies the time frame. It can be an integer value of seconds, or a string value with a time period appended after an integer.
s
- secondsm
- minutesh
- hoursd
- daysWhen using the string value for time representation - values can be combined.
Example: max: "1d5h30m10s"
The value of the interval option specifies either the time frame for the sliding window or the rate of time at which the token bucket is refilled, depending on which rate limiting algorithm you choose. Both have the same value options as the window configuration option.
The value of the capacity option specifies the maximum number of tokens a token bucket can hold.
The value of the refillRate
specifies how many tokens are to be added to the token bucket every interval cycle.
There are three different types of rate limiting algorithms offered by Arcjet:
A fixed limit of requests are allowed within a fixed window of time. This method utilizes the following configuration options: characteristics
, mode
, match
, window
and max
.
Example:
Requests made by a client are tracked against a sliding window (the window is continuously moving). This method utilizes the following configuration options: characteristics
, mode
, match
, interval
and max
.
Example:
Each request must withdraw a “token” from the “token bucket”. The bucket refills at the given interval and if there are no remaining tokens, the limit has been reached. This method utilizes the following configuration options: characteristics, mode
, match
, interval
, capacity
and refillRate
.
Example:
Svelte is a framework used to construct user interfaces. Reusable components written with HTML, CSS and JavaScript are written to .svelte
files. The Svelte compiler parses these files, analyzes them and generates a JavaScript file. Unlike other frontend frameworks, this occurs at build time rather than run time.
SvelteKit is an application framework built on top of Svelte to provide backend functionality. It adds additional features that are needed when building a full-stack application such as routing, data fetching, server side rendering, etc.
A detailed and interactive introduction of Svelte and its capabilities can be found here: https://learn.svelte.dev/tutorial/welcome-to-svelte.
The repository is available here: https://github.com/sveltejs/kit.
mkdir arcjet
cd arcjet
npm create svelte@latest ratelimit
Need to install the following packages: create-svelte@x.x.x Ok to proceed? (y)
- enter y
to proceed.cd ratelimit
npm install
npm i @arcjet/sveltekit
pnpm add @arcjet/sveltekit
yarn add @arcjet/sveltekit
.env.local
file in the project’s root. Add the following to this file:ARCJET_ENV=development
ARCJET_KEY=ajkey_YOUR-KEY-VALUE
To obtain a key create an Arcjet account or sign in - the key will be present in your account dashboard page. This Arcjet API key enables your application to use Arcjet, which can then be viewed on the Arcjet dashboard.
The environment is set to development
to ensure Arcjet knows it is running in a local environment. Arcjet will default to the production environment if it can’t determine if it is running locally. In production environments, private/internal addresses are not allowed, so we are explicitly setting the environment in order to be able to test with our locally.
To view the Arcjet SvelteKit SDK reference in its entirety, visit the docs.
Now that you are aware of just how diverse your rate limiting options are - let’s move onto development.
/src/lib/server/arcjet.ts
“Modules” refer to any JavaScript or TypeScript files that export functionality or data which can be imported and used in other parts of your application.
Modules in the /src/lib
directory are accessible to anything in the /src
directory using the $lib
alias. This feature simplifies imports from deeply nested routes by avoiding the need for directory traversal (../
).
Be aware that any presence of the server
keyword (either in a path or file name) means assets can only be imported by code that also runs server-side. This is to prevent accidentally importing sensitive data to the front-end.
The Arcjet instance will be defined in this file with an empty ruleset. Allowing you to define specific rules to each page using withRule
.
/src/hooks.server.ts
The hooks.server.ts
file applies rate limiting rules globally. This ensures that every request to your application is subject to these rules. It’s useful for implementing a base level of rate limiting across your entire application.
/src/routes/form/+page.svelte
Any +page.svelte
file inside /src/routes
creates a page in your application. Create a /form
directory and add a page.
The contents of the file (for now) will be:
/src/routes/form/+page.server.ts
The +page.server.ts
file handles server-side logic for a specific page or route in your SvelteKit application. Its scope is limited to the page or route where it is used.
Since it shares the /form
directory with +page.svelte
- it will handle the rate limiting for that page.
We will set tokenBucket
rate limiting on the page.
You are correct! The rule we just implemented on the form page via the +page.server.ts
file as well as the global rule.
To avoid this, you can exclude the form page from the global rule by editing the handle
function in the hooks.server.ts
file to resemble the following:
/src/routes/+layout.svelte
Let’s add a navigation bar to the top of each page using a +layout.svelte
file. This navigation bar will apply to every child and sister element in the /routes
directory. The <slot></slot>
element is where the page will be rendered.
Now, there is Arcjet rate limiting in place! To test your web application run npm run dev
and visit:
http://localhost:5173/ - This page has the global rule implemented. Refresh the page repeatedly. Per the rule, you were only allowed to make 5 requests!
http://localhost:5173/form - This page has the specific rule implemented. Again, refresh the page repeatedly. You should be rate limited after 3 requests!
Your Arcjet dashboard should resemble the following:
/src/lib/server/database.ts
The database.ts
file, for the sake of simplicity, creates an in-memory storage file instead of implementing a fully-fledged database.
A custom Message
type is defined - this sets the structure (what properties the message has) of the message object. An empty array is initialized and stored in the messageList
variable. This file exports two functions: getMessages()
and addMessage()
.
/src/routes/form/+page.svelte
Earlier we only added a header to the +page.svelte
file. Now, we will create a form. Submitting to this form will add a message to the messageList
array. All of the messages will be displayed in a list element on the page.
Notice the action="?/addToList"
attribute of the form - we will discuss this next.
/src/routes/form/+page.server.ts
The additions to the +page.server.ts
file handle the POST
request that is generated when the form is submitted.
In a standalone +server.ts
file, you can utilize functions corresponding to HTTP verbs such as GET or POST. SvelteKit form actions extends this concept by allowing you to define and give names to methods that correspond to specific actions. The action attribute in the form specifies the method to use - in this case addToList
. For more on form actions visit https://learn.svelte.dev/tutorial/the-form-element.
Take note of {requested: 1}
- when using the token bucket type, this is required and represents the number of tokens used per request.
Moving the protect
function (from the load
function) directly into the form action means the rate limiting will only apply to form submissions.
To test your web application run npm run dev
and visit:
http://localhost:5173/ - This page has the global rule implemented. Refresh the page repeatedly. Per the rule, you were only allowed to make 5 requests!
http://localhost:5173/form - This page has the specific rule implemented. Submit the form in quick succession. You should be rate limited after 3 requests!
Now you have a solid understanding of both SvelteKit and Arcjet's rate-limiting features! Every application is different, so feel free to adjust the configurations to best suit your needs. While you're there, check out some of Arcjet's other security features - they're equally easy to implement.
Get the full posts by email every week.