Same-Origin Policy and Cross-Origin Resource Sharing
Learning Objectives
- You know that browsers restrict programmatic cross-site interaction by default.
- You know of same-origin policy and cross-origin resource sharing.
Missing ‘Access-Control-Allow-Origin’ Header
Browsers restrict programmatic cross-site interaction by default, but allow it if a server explicitly tells that cross-site interaction is allowed. The Same-Origin Policy (SOP) of browsers restrict how content from one origin can interact with another origin, while Cross-Origin Resource Sharing (CORS) policy from servers informs browsers whether resources from outside the site’s domain can be interacted with.
When a cross-origin request is blocked due to missing or invalid CORS headers, the failure is not directly visible in the page but appears as an error in the developer console. A sample CORS error is shown in Figure 1 below.

The error stems from the fact that the server did not include the Access-Control-Allow-Origin header in its response. This header is required for browsers to allow JavaScript to access the response of a cross-origin request.
You can try this with the following component, which queries an open API for random startup ideas. If you open up the address https://itsthisforthat.com/api.php?json directly in the browser, you’ll see that it works fine. However, if you try to fetch the same URL from a Svelte component, you’ll see the CORS error in the console.
<script>
const API_URL = "https://itsthisforthat.com/api.php?json";
let idea = $state("");
const fetchIdea = async () => {
const res = await fetch(API_URL);
const data = await res.json();
idea = data?.this + " for " + data?.that;
};
</script>
<button onclick={fetchIdea}>Fetch idea</button>
<br/>
<p>Idea: {idea}</p>
Same-Origin Policy
To prevent unauthorized access and use of resources from different sites, browsers enforce the Same-Origin Policy. It restricts how a document or script loaded from one origin can interact with resources from another origin.
The term origin refers to the combination of protocol (scheme), domain (host), and port of a URL. For example, the origin of
https://example.com:8080/pathishttps://example.com:8080. Evenhttps://example.comandhttps://example.com:8080are considered different origins because they use different ports.
Under SOP, if JavaScript running on a page tries to fetch() a resource from a different origin, the browser will by default block the calling script from accessing the response. Some types of resources (images, stylesheets, <script> tags) are exempt from these restrictions, but programmatic access via JavaScript (e.g. fetch) is protected by SOP.
Web servers can explicitly allow cross-origin access by including CORS-specific headers in their responses, such as Access-Control-Allow-Origin. If such headers are missing or restrictive, the browser blocks the page’s JavaScript from using the response, and logs an error.
Figure 2 shows this process: the browser retrieves a page from one origin, then — following JavaScript instructions — requests JSON data from a different origin.
Cross-Origin Resource Sharing (CORS)
Cross-Origin Resource Sharing (CORS) is a mechanism that allows servers to tell browsers which cross-origin requests are permitted. Without CORS, the Same-Origin Policy prevents JavaScript on one origin from reading responses from another origin.
CORS headers
The server signals its CORS policy by including specific HTTP response headers. Browsers use these headers to decide whether JavaScript should be allowed to read the response.
The most common headers are:
Access-Control-Allow-Origin: Defines which origins are allowed. A value of*means “any origin is allowed” — but this cannot be used if credentials are included. To allow credentials, the value must be a specific origin, e.g.https://example.com.Access-Control-Allow-Methods: Lists allowed HTTP methods, e.g.GET, POST, PUT.Access-Control-Allow-Headers: Lists non-simple request headers that the client is allowed to send, e.g.Content-Type, Authorization.Access-Control-Allow-Credentials: Indicates whether the browser may include credentials (cookies, HTTP authentication, or client TLS certificates).
Other useful headers include Access-Control-Expose-Headers (to make certain response headers readable in JS) and Access-Control-Max-Age (to cache preflight results).
Simple and preflighted requests
CORS distinguishes between simple and preflighted requests.
A simple request meets all of these conditions:
- Method is
GET,HEAD, orPOST - Only “simple” request headers are set (
Accept,Accept-Language,Content-Language, etc.) - If there’s a request body,
Content-Typeisapplication/x-www-form-urlencoded,multipart/form-data, ortext/plain
For simple requests, the browser sends the request directly. After receiving the response, the browser checks the CORS headers and either allows JavaScript to read the data or blocks it.
A preflighted request occurs when any of the above conditions are not met — for example:
- Method is
PUT,DELETE, or something else not listed above - A non-simple header (like
Authorization) is sent - The
Content-Typeisapplication/json
In these cases, the browser automatically sends an HTTP OPTIONS request first. This preflight request includes:
OriginAccess-Control-Request-MethodAccess-Control-Request-Headers(if applicable)
The server then replies with CORS headers indicating whether the actual request is allowed, where the response must include Access-Control-Allow-Origin and Access-Control-Allow-Methods (and possibly others). The browser then checks these headers, and if the request is permitted, it sends the actual request. Otherwise, JavaScript cannot access the response, and a CORS error is logged in the browser console.
CORS is enforced by browsers for security. Tools like
curlor Postman will not block cross-origin requests — they will show the raw response regardless of CORS headers.
Allowing Cross-Origin Requests in Hono
Hono comes with a CORS Middleware, which allows easy configuration of cross-origin requests. The simplest way to use the CORS middleware is to allow all origins by using the cors() function without any parameters. This will add the necessary headers to the response, allowing cross-origin requests from any origin.
import { Hono } from "@hono/hono";
// importing cors middleware
import { cors } from "@hono/hono/cors";
const app = new Hono();
// using the CORS middleware to allow all origins
app.use('/*', cors());
// rest of the application code, e.g.
app.get('/count', (c) => {
return c.json({ count: 42 });
});
export default app;
Now, when we ask for the options from the server, we see a header access-control-allow-origin that defines the cross-origin policy. In this case, requests from browsers accessing sites at any origin are allowed to make requests to the server.
$ curl -X OPTIONS -v localhost:8000/count
...
< access-control-allow-methods: GET,HEAD,PUT,POST,DELETE,PATCH
< access-control-allow-origin: *
Summary
In summary:
- Browsers enforce the Same-Origin Policy (SOP) to restrict how scripts from one origin can interact with resources from another origin.
- CORS is a mechanism that allows servers to specify which cross-origin requests are permitted by including specific HTTP headers in their responses.
- Remember to check the browser console for CORS-related errors when debugging cross-origin request issues.
- Simple requests meet certain criteria and are sent directly, while preflighted requests require an initial
OPTIONSrequest to check permissions. - Hono provides built-in CORS middleware to easily configure cross-origin request policies for your server.