Adding CORS Support to Rust Rocket Applications

Last updated: Apr 13, 2025

1. Introduction: CORS and Rocket

When building web APIs with Rust and Rocket, you’ll often need to allow frontend applications running on
different domains (origins) to interact with your API. Browsers enforce the Same-Origin Policy, which prevents
this by default. To enable cross-origin requests, your Rocket server needs to implement Cross-Origin Resource
Sharing (CORS) by sending specific HTTP headers.

This guide explains how to add CORS support to your Rocket application, primarily using the popular
rocket_cors crate. We assume you have a basic Rocket application set up, similar to the one in
our Building a Simple Web API with Rust and Rocket tutorial.

2. What is CORS? (Quick Recap)

Cross-Origin Resource Sharing (CORS) is a security mechanism implemented by web browsers. It uses HTTP
headers sent by the server to determine if a web page from one origin (e.g.,
https://my-frontend.com) is allowed to make requests to a resource at a different origin (e.g.,
https://api.my-backend.com).

If the server doesn’t send the correct CORS headers (like Access-Control-Allow-Origin), the
browser will block the frontend JavaScript code from accessing the response, resulting in a CORS error in the
console. For a deeper dive, see Understanding CORS Errors and How to Fix
Them
.

3. Using therocket_corsCrate

While you could manually add CORS headers to every response in Rocket, it’s tedious and error-prone. The
community standard is to use the rocket_cors crate, which provides a Rocket “Fairing”
(middleware) to handle CORS headers automatically based on configuration.

3.1 Installation

Add rocket_cors to your Cargo.toml dependencies:

[dependencies]
rocket = { version = "0.5.0", features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
# Add rocket_cors
rocket_cors = "0.6.0" # Check crates.io for the latest compatible version

Or use cargo add:

cargo add rocket_cors

3.2 Basic Usage (Allowing All Origins)

The simplest way to get started is to allow requests from any origin (*).
Warning: This is often insecure for production APIs, especially those requiring
authentication or handling sensitive data. Use specific origins in production whenever possible.

Modify your src/main.rs to attach the CORS fairing:

#[macro_use] extern crate rocket;

use rocket::http::Method; // Import Method
use rocket_cors::{AllowedHeaders, AllowedOrigins, CorsOptions}; // Import Cors stuff

#[get("/")]
fn index() -> &'static str {
    "Hello, CORS World!"
}

#[get("/data")]
fn get_data() -> rocket::serde::json::Json {
    rocket::serde::json::Json(serde_json::json!({ "message": "This data is CORS enabled!" }))
}

#[launch]
fn rocket() -> _ {
    println!("🚀 Rocket launching with CORS at http://127.0.0.1:8000");

    // Configure CORS
    // Try default options first, which allows GET, POST, HEAD from any origin
    // let cors = rocket_cors::CorsOptions::default().to_cors()?; // Requires handling Result

    // A more explicit way to allow everything (use with caution!)
     let cors = CorsOptions::default()
        .allowed_origins(AllowedOrigins::all()) // Allow all origins
        .allowed_methods(
            vec![Method::Get, Method::Post, Method::Patch, Method::Delete, Method::Options] // Allow common methods
                .into_iter()
                .map(From::from)
                .collect(),
        )
        .allowed_headers(AllowedHeaders::all()) // Allow all headers
        .allow_credentials(true) // Allow cookies/auth (requires specific origin, not '*') - Be careful!
        .to_cors().expect("Failed to create CORS fairing."); // Use expect for simplicity here

    rocket::build()
        .mount("/", routes![index, get_data])
        .attach(cors) // Attach the CORS fairing
}

Key changes:

  • Import types fromrocket::httpandrocket_cors.

  • CreateCorsOptions.::default()provides sensible defaults, but we explicitly
    configure it here for clarity.

  • AllowedOrigins::all()allows any origin (*).

  • .allowed_methods(…)specifies permitted HTTP methods.

  • .allowed_headers(AllowedHeaders::all())permits any request header (needed for preflight
    checks with custom headers likeAuthorization).

  • .allow_credentials(true)is necessary if your frontend needs to send cookies or auth headers.
    Note: If you set this totrue, you cannot useAllowedOrigins::all(); you
    must specify exact origins instead for security reasons. The browser will rejectAccess-Control-Allow-Origin: *when credentials are involved. The example above showstruefor demonstration, but it would technically require a specific origin instead ofall()to work correctly with credentials in a browser.

  • .to_cors().expect(…)converts the options into a Fairing. We use.expect()for
    simplicity; proper error handling would use?ormatchin a real application.

  • .attach(cors)adds the CORS fairing to the Rocket instance.

3.3 Detailed Configuration

For production, configure CORS more strictly:

use rocket::http::{Method, Header};
use rocket_cors::{AllowedHeaders, AllowedOrigins, CorsOptions};
use std::str::FromStr;

// ... routes ...

#[launch]
fn rocket() -> _ {
     println!("🚀 Rocket launching with specific CORS at http://127.0.0.1:8000");

     // Define allowed origins
    let allowed_origins = AllowedOrigins::some_exact(&[
        "https://your-frontend-app.com",
        "http://localhost:3000", // For local development
    ]);

    let cors = CorsOptions {
        allowed_origins,
        allowed_methods: vec![Method::Get, Method::Post].into_iter().map(From::from).collect(),
        allowed_headers: AllowedHeaders::some(&[
            "Authorization",
            "Accept",
            "Content-Type", // Allow common headers
        ]),
        allow_credentials: true, // Allow cookies/auth headers
        // max_age: Some(3600), // Optional: Cache preflight response for 1 hour
        ..Default::default() // Use defaults for other options
    }
    .to_cors().expect("Failed to create CORS fairing.");

    rocket::build()
        .mount("/", routes![index, get_data]) // Add your actual routes
        .attach(cors)
}

This configuration:

  • Allows requests only fromhttps://your-frontend-app.comandhttp://localhost:3000.

  • Allows onlyGETandPOSTmethods.

  • Allows specific headers likeAuthorizationandContent-Type.

  • Explicitly allows credentials (remember, this requires specific origins, not*).

4. Understanding Rocket Fairings

Fairings are Rocket’s mechanism for intercepting and modifying requests and responses. They act like
middleware. The rocket_cors crate provides a pre-built fairing that automatically:

Intercepts incoming requests.
Checks if it’s a CORS preflight request (OPTIONS method with specific headers).
If preflight, responds appropriately based on the CorsOptions configuration.
If a regular request, allows it to proceed to the route handler.
Intercepts the outgoing response from the handler.
Adds the necessary Access-Control-Allow-* headers to the response based on the configuration
and the request’s Origin header.

By attaching the CORS fairing with .attach(cors), you enable this behavior for all routes
mounted on that Rocket instance.

5. Complete Example

Here’s a consolidated example combining a simple route and specific CORS configuration:

#[macro_use] extern crate rocket;

use rocket::http::Method;
use rocket_cors::{AllowedHeaders, AllowedOrigins, CorsOptions};
use rocket::serde::json::Json;
use serde::Serialize;

#[derive(Serialize)]
struct Message {
    content: String,
}

#[get("/")]
fn index() -> &'static str {
    "API Root"
}

#[get("/hello")]
fn hello() -> Json {
    Json(Message { content: "Hello from Rocket with CORS!".to_string() })
}

#[launch]
fn rocket() -> _ {
    println!("🚀 Rocket launching with specific CORS at http://127.0.0.1:8000");

    let allowed_origins = AllowedOrigins::some_exact(&[
        "http://localhost:5173", // Example: A Vite frontend dev server
        "https://my-production-frontend.com",
    ]);

    let cors = CorsOptions {
        allowed_origins,
        allowed_methods: vec![Method::Get].into_iter().map(From::from).collect(), // Only allow GET
        allowed_headers: AllowedHeaders::none(), // No custom headers needed for simple GET
        allow_credentials: false, // No credentials needed for this example
        ..Default::default()
    }
    .to_cors().expect("Failed to create CORS fairing.");

    rocket::build()
        .mount("/", routes![index, hello])
        .attach(cors)
}

6. Conclusion

Handling CORS in Rocket applications is essential for enabling cross-origin communication with frontend
applications. The rocket_cors crate provides a convenient and configurable fairing to manage the
required HTTP headers automatically.

Remember to configure CORS securely, especially in production, by specifying allowed origins, methods, and
headers precisely rather than using permissive wildcards, particularly when dealing with credentials or
sensitive data.

Additional Resources

Related Articles on InfoBytes.guru

External Resources