Skip to content

Install Qlyne on a Next.js app

For a Next.js app on Vercel, or on any host that runs Next.js, without Cloudflare. Qlyne runs as the app’s middleware: the same rules as the Cloudflare connector, decided before each page and API route, with the waiting room, the challenge, rate limits, blocked addresses, AI crawlers, login protection and access gates.

In the dashboard, open your site, Connector, “Install on Next.js”, and click Download qlyne-next.mjs. The file already carries your site ID, the Qlyne address and the key that checks the rules. Put it at the root of the project, next to package.json.

Create proxy.js at the root too (Next.js 16; on Next.js 13 to 15 the file is middleware.js, with the same content):

import { qlyne } from './qlyne-next.mjs';
export default qlyne();
export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'] };

The matcher keeps Next.js’s own files out, so they don’t count as visits. If the project already has a middleware, call Qlyne first and keep yours for what Qlyne lets through:

import { qlyne } from './qlyne-next.mjs';
const protect = qlyne();
export default async function proxy(request, event) {
const response = await protect(request, event);
if (!response.headers.get('x-middleware-next')) return response; // blocked, challenged or sent to the waiting room
return yourMiddleware(request, event);
}

Add the environment variable QLYNE_SECRET with the connector secret (in Connector, “Generate secret”). On Vercel: Settings, Environment Variables, for Production and Preview. Then deploy.

Leave the mode on “Observe only” at first and check the last 24 hours in the site Overview. When the numbers make sense, switch to “Enforce” in Connector. Changes reach the middleware within a minute, with no new deploy.

A middleware decides before the app answers, but never sees the answer. So:

RuleOn Next.js
Waiting room, challenge, under attack modeYes
Blocked and trusted addresses, Tor, your rulesYes; country comes from Vercel (x-vercel-ip-country), the network (ASN) is unknown, so a rule on it never matches
AI crawlersBlocked by user agent; the AI section of robots.txt is not added
Rate limitsCounted by the Qlyne server, adding up every instance of your app (one call to Qlyne on limited routes, about tens of milliseconds); if it doesn’t answer in time, each instance counts on its own until it’s back. qlyne({ rateLimit: 'memory' }) keeps the count in each instance only
Login protection, access gates, scanner trap, managed rules (WAF)Yes; the WAF reads form and JSON bodies up to 16 KB from a copy, so your app still gets the whole request
Protected formsNo: the middleware can’t add the hidden fields to the page. Use the form captcha
Security headers and CSPAdded to every response; the grade in the dashboard needs Cloudflare or the proxy
Signals to your appYes, as request headers (Qlyne-Country, Qlyne-Leaked-Password…)
Bot scoreYes, once the script is in the layout (below); the decoy page is served with a rewrite
Shared reputationYes, by the IP Vercel gives

Read the signals in a route handler or server component with headers() from next/headers.

The middleware can’t add the bot score script to your pages, so put it in the root layout (app/layout.js), inside <head>:

<script src="/__qlyne/s.js" async></script>

The middleware serves the script and takes its summary at /__qlyne/s; the matcher above already covers both paths. With the bot score off in the dashboard, they go to your app, which answers 404, and nothing else changes. What the score does is in the Cloudflare guide.

On Vercel the middleware reads the IP from x-real-ip and x-forwarded-for, which Vercel sets and the visitor can’t forge. On your own server, make sure the reverse proxy in front of Next.js (nginx, Caddy) overwrites those headers with the real address; otherwise a visitor could send a fake one and dodge the per-IP rules.

The version is on the first line of the file. To update, download it again, replace it and deploy. Your rules stay in the dashboard; the file only carries the code.