# Security Considerations and Best Practices for Deploying Astro Applications with SSR

> Secure your Astro SSR applications with origin validation, CSP generation, and payload limits. Learn best practices to prevent cross-origin attacks, XSS, and DoS.

- Repository: [Astro/astro](https://github.com/withastro/astro)
- Tags: best-practices
- Published: 2026-03-06

---

**Astro's built-in `security` configuration block provides origin validation, Content Security Policy generation, and request payload limits to protect SSR deployments from cross-origin attacks, XSS, and denial-of-service vectors.**

Astro's server-side rendering (SSR) architecture executes your pages on Node-compatible or Edge runtimes, creating a traditional server attack surface that requires careful hardening. The withastro/astro repository includes a comprehensive **security** configuration system defined in [`packages/astro/src/types/public/config.ts`](https://github.com/withastro/astro/blob/main/packages/astro/src/types/public/config.ts) that enforces protection at both build-time and runtime. Understanding these security considerations and best practices for deploying Astro applications with SSR is essential for maintaining a secure production environment.

## Core Security Configuration Options

Astro exposes a `security` object in `astro.config.mjs` that controls three primary defense mechanisms: origin validation, Content Security Policy generation, and request body limits.

### Origin Validation with checkOrigin

The **`security.checkOrigin`** option validates incoming request headers against an explicit whitelist. When enabled (the default for SSR), Astro inspects the `Origin` and `Sec-Fetch-Site` headers and rejects requests from unauthorized domains with a 403 response.

In [`packages/astro/src/vite-plugin-astro-server/sec-fetch.ts`](https://github.com/withastro/astro/blob/main/packages/astro/src/vite-plugin-astro-server/sec-fetch.ts), the middleware reads `settings.config.security?.allowedDomains` to perform this validation. The **`security.allowedDomains`** array accepts glob-style patterns such as `["example.com", "*.trusted.com"]`. Leaving this as the default empty array `[]` removes all restrictions, which is dangerous for public-facing SSR sites.

### Content Security Policy Generation

The **`security.csp`** option enables automatic hash generation for inline scripts and styles. During the build process, [`packages/astro/src/vite-plugin-astro-server/plugin.ts`](https://github.com/withastro/astro/blob/main/packages/astro/src/vite-plugin-astro-server/plugin.ts) walks the compiled HTML, extracts inline `<script>` and `<style>` blocks, hashes them using SHA-256 (configurable via `security.csp.algorithm`), and injects the appropriate directives.

Key CSP sub-options include:

- **`strictDynamic`**: When `true`, adds the `strict-dynamic` directive to prevent unsafe script execution
- **`directives`**: Custom policy definitions for `script-src`, `style-src`, and other CSP categories

Adapters such as Vercel ([`packages/integrations/vercel/src/index.ts`](https://github.com/withastro/astro/blob/main/packages/integrations/vercel/src/index.ts)) and Node ([`packages/integrations/node/src/index.ts`](https://github.com/withastro/astro/blob/main/packages/integrations/node/src/index.ts)) automatically propagate these policies to the `Content-Security-Policy` HTTP header for edge functions.

### Action Payload Protection

The **`security.actionBodySizeLimit`** parameter prevents denial-of-service attacks via oversized POST requests. The default value is `1_048_576` bytes (1 MiB). In [`packages/astro/src/vite-plugin-astro-server/plugin.ts`](https://github.com/withastro/astro/blob/main/packages/astro/src/vite-plugin-astro-server/plugin.ts), requests exceeding this limit receive an immediate 413 response before reaching your application logic.

## Runtime Security Architecture

Understanding how Astro enforces these configurations at runtime helps you debug security violations and optimize your defense posture.

### Request Origin Verification

When `checkOrigin` is enabled, the middleware in [`sec-fetch.ts`](https://github.com/withastro/astro/blob/main/sec-fetch.ts) executes before route handlers. It compares the request's origin against your `allowedDomains` patterns using glob matching. Mismatches trigger an early abort with a clear error message logged via Astro's internal logger.

### CSP Hash Injection

During SSR rendering, the Vite plugin inspects the generated HTML response body. It computes SHA-256 hashes for every inline script and style block, then constructs a CSP header or `<meta>` tag that includes these hashes in the `script-src` and `style-src` directives. This prevents XSS attacks by ensuring only cryptographically verified inline code executes.

### Adapter-Level Header Enforcement

SSR adapters read the finalized `security.csp` configuration during deployment. For example, the Vercel adapter constructs the complete CSP string and sets it as a response header in edge functions. This ensures consistent policy enforcement regardless of whether you deploy to serverless functions, edge workers, or traditional Node servers.

## Production Deployment Checklist

Follow these specific steps to secure your Astro SSR deployment:

1. **Enable origin checking** – Keep `security.checkOrigin: true` and explicitly define `allowedDomains` without wildcards:

   ```js
   // astro.config.mjs
   export default {
     security: {
       checkOrigin: true,
       allowedDomains: ["example.com", "*.api.example.com"]
     }
   };
   ```

2. **Activate CSP with strict defaults** – Enable automatic hash generation and avoid unsafe-inline directives:

   ```js
   security: {
     csp: {
       directives: {
         "script-src": ["'self'", "https://cdn.jsdelivr.net"],
         "style-src": ["'self'", "https://fonts.googleapis.com"]
       },
       strictDynamic: true
     }
   }
   ```

3. **Restrict payload sizes** – Maintain the default 1 MiB limit for Astro Actions unless your business logic specifically requires larger uploads.

4. **Validate environment exposure** – Ensure secret variables use the `PRIVATE_` prefix and never appear in client bundles. Astro's build process automatically filters these from the client-side JavaScript.

5. **Test security headers** – After building, verify CSP hashes appear in responses:

   ```bash
   curl -I https://my-site.com/
   # Expect: Content-Security-Policy: script-src 'self' 'sha256-abc123...'

   ```

6. **Monitor origin violations** – Run `astro dev --site https://example.com` locally to observe blocked origin attempts in the server logs emitted by the security middleware.

## Practical Configuration Examples

### Minimal Secure SSR Setup

```js
// astro.config.mjs
import { defineConfig } from "astro/config";

export default defineConfig({
  output: "server",
  adapter: "node",
  security: {
    checkOrigin: true,
    allowedDomains: ["example.com", "*.trusted-cdn.com"],
    actionBodySizeLimit: 500_000,
    csp: {
      directives: {
        "script-src": ["'self'", "https://cdn.jsdelivr.net"],
        "style-src": ["'self'", "https://fonts.googleapis.com"],
        "img-src": ["'self'", "data:", "https://images.example.com"]
      },
      strictDynamic: true
     }
  }
});

```

### Adapter-Specific CSP Implementation

The Vercel adapter demonstrates how security configurations translate to edge function headers:

```js
// packages/integrations/vercel/src/index.ts (conceptual excerpt)
if (config.security?.csp) {
  const cspHeader = buildCspHeader(config.security.csp);
  responseHeaders.set("content-security-policy", cspHeader);
}

```

### Runtime Payload Enforcement

The server-side request parser implements the size limit check:

```js
// packages/astro/src/vite-plugin-astro-server/plugin.ts (conceptual excerpt)
if (settings.config.security?.actionBodySizeLimit) {
  const limit = settings.config.security.actionBodySizeLimit;
  if (request.body?.length > limit) {
    return new Response("Payload too large", { status: 413 });
  }
}

```

## Summary

- **Origin validation** via `security.checkOrigin` and `allowedDomains` prevents unauthorized cross-origin requests at the middleware level.
- **CSP generation** automatically hashes inline scripts in [`packages/astro/src/vite-plugin-astro-server/plugin.ts`](https://github.com/withastro/astro/blob/main/packages/astro/src/vite-plugin-astro-server/plugin.ts), with adapters propagating policies to HTTP headers.
- **Payload limits** default to 1 MiB and reject oversized Action requests with 413 status codes.
- **Environment variable hygiene** requires the `PRIVATE_` prefix to keep secrets server-side only.
- **Source locations** for security logic include [`packages/astro/src/types/public/config.ts`](https://github.com/withastro/astro/blob/main/packages/astro/src/types/public/config.ts) (schema), [`sec-fetch.ts`](https://github.com/withastro/astro/blob/main/sec-fetch.ts) (origin checking), and adapter packages like [`packages/integrations/vercel/src/index.ts`](https://github.com/withastro/astro/blob/main/packages/integrations/vercel/src/index.ts).

## Frequently Asked Questions

### What is the default security configuration for Astro SSR?

By default, Astro enables `security.checkOrigin: true` when `output: "server"` is configured, but leaves `allowedDomains` as an empty array and disables CSP. This means origin checking is active but unrestricted unless you explicitly define allowed domains. The `actionBodySizeLimit` defaults to 1 MiB to protect against basic denial-of-service attacks.

### How does Astro validate request origins in SSR mode?

Astro validates origins in [`packages/astro/src/vite-plugin-astro-server/sec-fetch.ts`](https://github.com/withastro/astro/blob/main/packages/astro/src/vite-plugin-astro-server/sec-fetch.ts) by comparing the `Origin` and `Sec-Fetch-Site` headers against your `security.allowedDomains` patterns using glob matching. If `checkOrigin` is true and the origin does not match, the request aborts immediately with a 403 response before reaching your page components or API routes.

### What is the recommended CSP configuration for production Astro sites?

Enable `security.csp` with `strictDynamic: true` to prevent unsafe script execution, and explicitly whitelist only required external domains in your directives. Allow Astro to generate SHA-256 hashes automatically for inline scripts rather than using `'unsafe-inline'`. Verify the generated hashes appear in the `Content-Security-Policy` header using curl or browser dev tools after deployment.

### How can I prevent large payload attacks on Astro Actions?

Set a conservative `security.actionBodySizeLimit` value in bytes within your Astro configuration. The runtime enforces this limit in [`packages/astro/src/vite-plugin-astro-server/plugin.ts`](https://github.com/withastro/astro/blob/main/packages/astro/src/vite-plugin-astro-server/plugin.ts) by checking request body length and returning a 413 status for violations. Never set this limit to `Infinity`, and adjust it only if your specific use case requires file uploads or large data submissions.