Simplify Authentication by Replacing Nginx with Cloudflare Snippets
We have been using Nginx-based authentication to validate JWT tokens and SSO session cookies for a while now. This setup is used both with Nginx as a reverse proxy in front of containerized applications outside Kubernetes and in the Kubernetes Ingress Controller (ingress-nginx). Although Nginx is a powerful reverse proxy, its poor documentation and complex rules engine have caused us to waste a lot of time on simple tasks like CORS, which are easy to handle with Cloudflare snippets. We plan to migrate from the Kubernetes Ingress API to the Gateway API soon, as the Gateway API is the future of the Ingress API. Due to our past challenges with Nginx as an ingress controller, we have decided to move only the routes from Ingress to the Gateway API and manage everything else using Cloudflare snippets. Another reason is that we want to reduce latency for common concerns (e.g., rate limiting, authentication, CORS, etc.) in our applications by handling these at the edge. What is Cloudflare Snippets? A Cloudflare Snippet is a lightweight, customizable piece of code that allows users to apply specific rules or modifications to web traffic on websites protected by Cloudflare, without needing to alter the site’s core codebase. Introduced as part of Cloudflare’s suite of tools, Snippets enable quick, serverless execution of small scripts at the edge, meaning they run on Cloudflare’s global network of servers rather than the origin server. This can be used for tasks like redirecting traffic, modifying HTTP headers, or implementing A/B testing, all while maintaining performance and scalability. Snippets are particularly useful for developers and site administrators who want granular control over how requests and responses are handled, offering a flexible alternative to more complex solutions like Cloudflare Workers, but with simpler deployment for smaller-scale tasks. Essentially, it’s a way to tweak web behavior efficiently, leveraging Cloudflare’s infrastructure. Why replace nginx based authentication with Cloudflare Snippets? When a client accesses a protected REST API, the first step is to validate the SSO cookie (for web applications) or the JWT token (for CLI or other programs). This validation can be performed at several points, as shown in the diagram below. The diagram above shows a complex setup where REST APIs, developed with a framework, run inside an application server on a Kubernetes cluster. The Kubernetes cluster's ingress is exposed to the internet through a Cloudflare tunnel, providing a secure way to make a service accessible online without opening firewall ports for inbound traffic. We will share a dedicated post to explain the benefits of Cloudflare tunnels for developers. In this setup, there are multiple points where authentication logic can be implemented using interceptors or filters, indicated by numbers 1 to 4 in the diagram. While "request interceptors" or "filters" are terms used in the Java world for components that execute logic before a request reaches any REST API endpoints, they may have different names in other programming languages or frameworks, such as the before_request decorator in Flask. Depending on the situation, you might handle SSO cookie or access/ID token validation (passed as a JWT token) in different places, so it's important to know all the options for handling this logic. SSO cookie validation is needed when a REST application request comes from a web application, while JWT token validation is required for requests from non-web application clients. In a development environment, you might be working on the REST API without an application server or with a REST API framework that doesn't involve an application server. For quick development, you can perform this validation using an interceptor/filter provided by the REST API framework to speed up development. If you are testing multiple REST APIs within an application server, you can perform this validation using an interceptor/filter at the application server level. If you are testing multiple REST APIs within a Kubernetes cluster, it makes sense to perform this validation at the ingress controller level (e.g., using an auth URL for controllers based on Nginx). If remote development and other environments (e.g., Non-Prod or Prod) are only accessible through the Cloudflare network, it makes sense to perform this validation at the edge, closer to the location of your application's users. Validation at the edge using Cloudflare snippets offers several benefits: It has minimal delay because it runs in a data center close to the user's location. Although Cloudflare snippets are available only for paid plans, there is no extra cost for paid users. Authentication is a common concern for all applications, so centralizing it reduces the maintenance needed when handling it in different software. It also makes things easier for developers, as they only need to learn one method to handle a global conce
We have been using Nginx-based authentication to validate JWT tokens and SSO session cookies for a while now. This setup is used both with Nginx as a reverse proxy in front of containerized applications outside Kubernetes and in the Kubernetes Ingress Controller (ingress-nginx). Although Nginx is a powerful reverse proxy, its poor documentation and complex rules engine have caused us to waste a lot of time on simple tasks like CORS, which are easy to handle with Cloudflare
snippets.
We plan to migrate from the Kubernetes Ingress API
to the Gateway API
soon, as the Gateway API
is the future of the Ingress API. Due to our past challenges with Nginx as an ingress controller, we have decided to move only the routes from Ingress to the Gateway API and manage everything else using Cloudflare snippets. Another reason is that we want to reduce latency for common concerns (e.g., rate limiting, authentication, CORS, etc.) in our applications by handling these at the edge.
What is Cloudflare Snippets?
A Cloudflare
Snippet is a lightweight, customizable piece of code that allows users to apply specific rules or modifications to web traffic on websites protected by Cloudflare, without needing to alter the site’s core codebase. Introduced as part of Cloudflare’s suite of tools, Snippets enable quick, serverless execution of small scripts at the edge, meaning they run on Cloudflare’s global network of servers rather than the origin server. This can be used for tasks like redirecting traffic, modifying HTTP headers, or implementing A/B testing, all while maintaining performance and scalability. Snippets are particularly useful for developers and site administrators who want granular control over how requests and responses are handled, offering a flexible alternative to more complex solutions like Cloudflare Workers, but with simpler deployment for smaller-scale tasks. Essentially, it’s a way to tweak web behavior efficiently, leveraging Cloudflare’s infrastructure.
Why replace nginx based authentication with Cloudflare Snippets?
When a client accesses a protected REST API, the first step is to validate the SSO cookie (for web applications) or the JWT token (for CLI or other programs). This validation can be performed at several points, as shown in the diagram below.
The diagram above shows a complex setup where REST APIs, developed with a framework, run inside an application server on a Kubernetes cluster. The Kubernetes cluster's ingress is exposed to the internet through a Cloudflare tunnel, providing a secure way to make a service accessible online without opening firewall ports for inbound traffic. We will share a dedicated post to explain the benefits of Cloudflare tunnels for developers.
In this setup, there are multiple points where authentication logic can be implemented using interceptors or filters, indicated by numbers 1 to 4 in the diagram. While "request interceptors" or "filters" are terms used in the Java world for components that execute logic before a request reaches any REST API endpoints, they may have different names in other programming languages or frameworks, such as the before_request
decorator in Flask.
Depending on the situation, you might handle SSO cookie or access/ID token validation (passed as a JWT token) in different places, so it's important to know all the options for handling this logic. SSO cookie validation is needed when a REST application request comes from a web application, while JWT token validation is required for requests from non-web application clients.
In a development environment, you might be working on the REST API without an application server or with a REST API framework that doesn't involve an application server. For quick development, you can perform this validation using an interceptor/filter provided by the REST API framework to speed up development.
If you are testing multiple REST APIs within an application server, you can perform this validation using an interceptor/filter at the application server level.
If you are testing multiple REST APIs within a Kubernetes cluster, it makes sense to perform this validation at the ingress controller level (e.g., using an auth URL for controllers based on Nginx).
If remote development and other environments (e.g., Non-Prod or Prod) are only accessible through the Cloudflare network, it makes sense to perform this validation at the edge, closer to the location of your application's users.
Validation at the edge using Cloudflare snippets offers several benefits:
It has minimal delay because it runs in a data center close to the user's location.
Although Cloudflare snippets are available only for paid plans, there is no extra cost for paid users.
Authentication is a common concern for all applications, so centralizing it reduces the maintenance needed when handling it in different software.
It also makes things easier for developers, as they only need to learn one method to handle a global concern instead of learning it for different software or programming languages.
In the case of a multi-tenant SaaS application, you can validate an SSO cookie or JWT token against the IdP configured for a specific tenant. This is useful when a SaaS application needs to support different IdPs for different tenants, especially if these IdPs differ from the one used by the SaaS application.
Handling authentication using Cloudflare Snippets
In this post, we introduced a universal approach to handle SSO using a single oauth2-proxy
, eliminating the need to register multiple applications in your IdP. The following code is for a snippet that performs SSO cookie or access/id token validation using oauth2-proxy
. However, you can adjust this code for any setup you may have for your applications.
export default {
async fetch(request, env, ctx) {
// Extract JWT from Authorization header
const authHeader = request.headers.get('Authorization');
const token = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : null;
// Extract _oauth2_proxy cookie if no JWT
const cookieHeader = request.headers.get('cookie');
const oauth2Cookie = cookieHeader?.match(/(?:^|;\s*)_oauth2_proxy=([^;]*)/)?.[1];
// If no credentials provided
if (!token && !oauth2Cookie) {
return new Response('Forbidden - Invalid credentials', {
status: 403
});
}
// Validation endpoint using /auth
const validationUrl = 'https://oauth2-proxy.example.com/oauth2/auth';
const validationHeaders = new Headers({
'Content-Type': 'application/json'
});
// Set appropriate auth header based on what's provided
if (token) {
validationHeaders.set('Authorization', `Bearer ${token}`);
} else if (oauth2Cookie) {
validationHeaders.set('Cookie', `_oauth2_proxy=${oauth2Cookie}`);
}
try {
// Validate with oauth2-proxy /auth endpoint
const response = await fetch(validationUrl, {
method: 'GET',
headers: validationHeaders
});
if (response.status !== 202) {
return new Response('Forbidden - Invalid credentials', {
status: 403
});
}
// Get Authorization header and user email from oauth2-proxy response
const proxyAuthHeader = response.headers.get('Authorization');
const userEmail = response.headers.get('X-Auth-Request-Email') || 'authenticated';
// Create new headers object with only what we want to forward
const modifiedHeaders = new Headers();
// Set Authorization header from oauth2-proxy response if present
if (proxyAuthHeader) {
modifiedHeaders.set('Authorization', proxyAuthHeader);
}
// Set user info header if available
if (userEmail) {
modifiedHeaders.set('X-Authenticated-User', userEmail);
}
// Create modified request with only our specified headers
const modifiedRequest = new Request(request, {
headers: modifiedHeaders
});
// Proceed with the modified request to origin
return await fetch(modifiedRequest);
} catch (error) {
return new Response(`Authentication error: ${error.message}`, {
status: 503
});
}
}
};
In the code above, we are using the /auth
endpoint of oauth2-proxy
. Although this endpoint is designed to be used as an nginx auth URL, it can also be used elsewhere to validate an SSO cookie or ID (JWT) token.
This endpoint returns a 202 status for a valid SSO cookie or ID (JWT) token. If not, it returns a 401 status. The endpoint provides Authorization
, x-auth-request-email
, and x-auth-request-user
headers, which include the JWT token, user email ID, and a unique ID generated for the user record by the IdP, respectively. These headers are then forwarded to the origin.
The Nginx auth module stops the request if it encounters an invalid token (401 status code from the /auth
endpoint), similar to how it is handled in the snippet above. However, in MechCloud, we have a special requirement where the request should always be forwarded to the origin server, regardless of the status code returned by the /auth
endpoint. This is because, in MechCloud, a site page can be either public or protected. So, MechCloud cannot serve public site pages if Nginx stops the request for an anonymous user. Implementing this simple requirement took me more than a day because we couldn't find a working configuration and had to use trial and error to figure it out. The same issue occurred when we wanted to implement CORS for our REST APIs. To meet this basic requirement with the snippet above, you just need to disable the logic wherever it returns a 403 status code.
Deploy this snippet under a zone and set up a rule to specify when it should run. Since this snippet should at least run for all API endpoints, you can create a custom filter if your API hostnames follow a specific pattern (e.g., start with -api.example.com).
Example expression - (http.host strict wildcard "*-api.example.com")
The expression above ensures that this snippet runs for all API endpoints where the hostname ends with -api.example.com
.
Conclusion
Migrating from Nginx-based authentication to Cloudflare Snippets offers a streamlined approach to managing authentication at the edge, reducing latency and simplifying maintenance. By leveraging Cloudflare's global network, you can efficiently handle common concerns like authentication, rate limiting, and CORS, providing a consistent and centralized solution. This transition not only enhances performance but also eases the developers' workload by unifying the authentication process across different environments and applications. As you move towards adopting the Gateway API, integrating Cloudflare Snippets ensures that your infrastructure remains agile and future-proof, ready to meet the evolving demands of modern web applications.