Today, it is becoming more common to decouple your frontend (usually a JS framework) and your backend (Node.js, PHP, Python, etc.) or even leverage micro-services. However, web browsers implement security measures that restrict such cross-origin requests by default. This is whereDocumentation Index
Fetch the complete documentation index at: https://developer.upsun.com/llms.txt
Use this file to discover all available pages before exploring further.
Cross-Origin Resource Sharing (CORS) comes into play.
Understanding CORS is crucial for developers building modern web applications, as it allows you to access resources from different origins while maintaining security safely. Let’s dive into the world of CORS and explore its key concepts.
Fundamentals
The Same-Origin Policy
Before we delve into CORS, it’s essential to understand the Same-Origin Policy, which is the foundation of web security.
The Same-Origin Policy is a critical security mechanism implemented by web browsers. It restricts web pages from making requests to a domain different from the one serving the web page. An origin is considered the same if the protocol, domain, and port all match.
For example:
https://example.com/page1andhttps://example.com/page2have the same originhttps://example.comandhttp://example.comhave different origins (different protocol)https://example.comandhttps://example.com:3000have different origins (different port)https://example.comandhttps://api.example.comhave different origins (different subdomain)
Same-Origin Policy is important because it prevents malicious scripts on one page from obtaining access to sensitive data on another page through the Document Object Model (DOM).
However, this policy can be too restrictive for legitimate cross-origin requests, which is where CORS comes in.
What is CORS?
Cross-Origin Resource Sharing (CORS) is a mechanism that allows many resources (e.g., fonts, JavaScript, etc.) on a web page to be requested from another domain outside the domain from which the resource originated.
CORS works by adding new HTTP headers that allow servers to describe which origins are permitted to read that information from a web browser. It’s a way to relax the Same-Origin Policy in a controlled manner.
For example, if a user visits https://example.com and that page wants to make an AJAX request to https://api.example.com, it will be blocked by default due to the Same-Origin Policy. CORS provides a way for the server at https://api.example.com to say, “I will accept requests from https://example.com.”
CORS Headers
CORS is implemented through a series of HTTP headers. The most important ones are:
Access-Control-Allow-Origin: Specifies which origins can access the resource. It can be a specific origin,"*"for any origin, or null.Access-Control-Allow-Methods: Specifies the HTTP methods (GET,POST, etc.) allowed when accessing the resource.Access-Control-Allow-Headers: Indicates which HTTP headers can be used during the actual request.Access-Control-Allow-Credentials: Indicates whether the response to the request can be exposed when the credentials flag is true.
https://example.com is allowed to make GET, POST, and PUT requests to this resource and can include the Content-Type header in its requests.
Understanding these headers is important because they form the core of how CORS works. As a developer, you must configure your application to send the appropriate headers based on your security requirements.
Simple vs. Preflight Requests
CORS requests can be divided into two categories: simple requests and preflight requests.
Simple requests don’t trigger a CORS preflight. They are typically GET, HEAD, or POST requests with only simple headers. The browser sends these directly, and the server can respond with CORS headers.
Preflight requests are used for more complex HTTP requests. Before sending the actual request, the browser first sends an HTTP OPTIONS request to the server, asking for permission.
For example, a PUT request with a custom header would trigger a preflight request:
CORS headers, telling the browser whether the actual request is allowed.
Understanding the difference between simple and preflight requests is important because it affects how your server needs to handle CORS requests and can impact the performance of your application.
CORS and Security
While CORS allows for more flexible web applications, it’s important to understand that it’s not a security feature in itself. CORS doesn’t protect your server from unauthorized access; it tells the browser to allow certain cross-origin requests.
Proper security measures, such as authentication and authorization, should always be implemented on the server side. CORS should be configured to allow only the necessary origins, methods, and headers.
For example, instead of using Access-Control-Allow-Origin: *, which allows any origin, you should specify the exact origins:
Configuration Examples
Symfony (PHP)
In Symfony, you can use the NelmioCorsBundle to handleCORS.
Here’s an example configuration:
-
Install the bundle:
-
Configure
CORSin yourconfig/packages/nelmio_cors.yaml: -
Set the
CORS_ALLOW_ORIGINenvironment variable in your.envfile:
Next.js (JavaScript)
In Next.js, you can configureCORS by creating a custom API route.
Here’s an example:
-
Create a file
pages/api/cors.js: -
Create the
lib/init-middleware.jsfile: -
Install the
corspackage:
CORS to the specific API route. You can adjust the options in the Cors() function call to fit your needs.
Certainly! I’ll add an example for Laravel to the CORS Configuration Examples section. Here’s the updated section with Laravel included:
Laravel (PHP)
Laravel provides built-in support forCORS through its middleware. Here’s how you can configure CORS in a Laravel application:
-
Laravel 7 and above come with
CORSsupport out of the box. For earlier versions, you may need to install thefruitcake/laravel-corspackage: -
Publish the
CORSconfiguration file:This will create aconfig/cors.phpfile. -
Configure
CORSinconfig/cors.php:This configuration:- Applies
CORSto all routes under/api - Allows all HTTP methods
- Allows requests from
http://localhost:3000andhttps://example.com - Allows all headers
- Doesn’t expose any headers
- Doesn’t set a max age for preflight requests
- Doesn’t allow credentials (cookies, HTTP authentication) in
CORSrequests
- Applies
-
If you need more fine-grained control, you can create a
CORSmiddleware for specific routes. Create a fileapp/Http/Middleware/Cors.php:Then, register this middleware inapp/Http/Kernel.php:You can now apply this middleware to specific routes or route groups:
CORS for your entire application or for specific routes. Remember to adjust the allowed origins, methods, and headers according to your application’s needs and security requirements.
Certainly! I’ll add a new section to the article about querying an API protected with CORS using curl and Postman. Here’s the new section:
Querying CORS-Protected APIs
While CORS is primarily a browser security feature, it’s often useful to test APIs directly using tools like curl or Postman. Here’s how you can work with CORS-protected APIs using these tools:
Using curl
curl doesn’t enforceCORS restrictions, as it’s not a web browser. However, to simulate a CORS request or to test your server’s CORS configuration, you can send the appropriate headers with your request.
-
Simple GET request:
This sends a preflight
OPTIONSrequest. The server’s response will include theCORSheaders if it’s configured correctly. -
POST request with custom headers:
This sends a
POSTrequest with a custom header, which would typically trigger a preflight request in a browser.
Using Postman
Postman is more user-friendly for testing APIs and provides a GUI for constructing requests.-
Setting up a
CORSrequest in Postman:- Open a new request in Postman
- Set the HTTP method (
GET,POST, etc.) - Enter the API URL
- Go to the “Headers” tab
- Add the following headers:
- Key:
Origin, Value:http://example.com - Key:
Access-Control-Request-Method, Value:POST(or whatever method you’re using) - Key:
Access-Control-Request-Headers, Value:Content-Type, X-Requested-With(or whatever headers you’re using)
- Key:
-
Sending a preflight request:
- Set the method to
OPTIONS - Send the request
- Check the response headers for
CORS-relatedheaders
- Set the method to
-
Sending the actual request:
- Set the method back to your intended method (
GET,POST, etc.) - If it’s a
POSTrequest, go to the “Body” tab and add your payload - Send the request
- Set the method back to your intended method (
Interpreting the Results
When testing with curl or Postman, pay attention to the response headers. Look for:Access-Control-Allow-Origin: Should match yourOriginor be'*'Access-Control-Allow-Methods: Should include the method you’re usingAccess-Control-Allow-Headers: Should include any custom headers you’re sending
CORS requests from your specified origin.
Important Notes
-
Remember that curl and Postman don’t enforce
CORSpolicies. They’ll send the request regardless of the server’sCORSconfiguration. This is useful for testing but doesn’t reflect how a web browser would behave. - If you’re testing an API that requires authentication, make sure to include the necessary authentication headers or tokens in your requests.
-
Some APIs might have additional security measures beyond
CORS, such as CSRF tokens. You may need to account for these in your tests. - When testing in production environments, be cautious about sending too many requests, as this might trigger rate limiting or security alerts.
CORS configurations on your APIs, ensuring they’re set up correctly before integrating them into your web applications. This approach can save a lot of time and frustration when dealing with CORS issues in development.
Platform.sh & Upsun Configuration
When working with Platform.sh or Upsun, you can dynamically set the allowed origins forCORS based on the $PLATFORM_ROUTES environment variable. This is particularly useful for handling multiple environments (development, staging, production) with different URLs.
Here’s an example of how you might parse the $PLATFORM_ROUTES variable and use it to set the allowed origins:
CORS configuration remains correct across all your environments without manual intervention.