Last updated: April 13, 2025
Table of Contents
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 the rocket_cors
Crate
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 from
rocket::http
androcket_cors
. - Create
CorsOptions
.::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 showstrue
for 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?
ormatch
in 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 from
https://your-frontend-app.com
andhttp://localhost:3000
. - Allows only
GET
andPOST
methods. - Allows specific headers like
Authorization
andContent-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'sOrigin
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.
7. Additional Resources
Related Articles
- Getting Started with Rust
- Rust Ownership and Borrowing Explained
- Understanding CORS Errors and How to Fix Them
- Building a Simple Web API with Rust and Rocket