Blog
Most probes against this Next.js site hunted for leaked secrets and software it never ran
Four weeks of proxy logs from this site show probes hunting for WordPress, PHP and leaked secrets, while the Next.js flaws that matter arrive as well-formed requests.
The site you are reading, custralis.com, is a Next.js application built and run by Custralis. It runs on Node in a container behind a reverse proxy, and the proxy’s access logs for the 28 days from 16 August to 12 September 2026 are the material for this case study. No IP address appears here. The counts were computed on the server, and only totals left it.
WordPress has never run here, and neither has PHP. In those four weeks /wp-admin/install.php was still requested 270 times, from 61 different addresses.
What was counted
Two kinds of request go into the numbers. The first is every plain-HTTP request that named no host served here, whether its Host header held an IP address, was empty or carried a hostname the server does not serve. That is how a scanner sweeping address ranges arrives. Plain-HTTP requests to the site’s own hostnames only get a redirect to HTTPS, and they stay out unless they also fall into the second kind. The second is any request, to any host, whose path or query string contains one of the substrings in a written list of probe patterns (.env, .git, wp-, .php, ../ and the like), plus requests with unusual methods such as CONNECT or TRACE, and POSTs carrying a Next-Action header to any path other than the two contact-form URLs. The definitions were written before counting. The count itself is a script kept in the site’s repository, and a second script, written independently from the same definitions, produced the same totals.
Left out are the other 403s on the site’s own hostnames. Almost all of them went to addresses that the intrusion-prevention layer had already banned, and a 403 of that kind shows only that the address had misbehaved earlier, not what that particular request wanted.
On that basis the proxy logged 32,897 requests from 1,901 distinct addresses. Not one got a successful response. 87.0% were refused with a 403. Most of the rest were redirects, nearly all of them from www to the main host or from HTTP to HTTPS, and 342 ended in a 404, 405 or 400. Each logged line counts once, so a probe that follows a redirect shows up twice. Volume came in waves, with the quietest day, 29 August, at 240 of these requests and the busiest, 7 September, at 3,032, more than ten times as many.
Of the 1,901 addresses, 1,199 (63.1%) belong to networks whose registered names mark them as hosting, cloud or data-centre providers, and 6 to recognisable home or mobile carriers. The other 696 could not be classified by name, and 148 of those have no network owner in the database at all. The data-centre share is therefore a floor.
What the scanners were after
The 25,049 requests that matched the pattern list came from 649 addresses, and each is assigned to one family only, checked in a fixed order starting with Next.js and React, so the rows add up to that total.
| Family | Requests | Share | Addresses | Typical paths |
|---|---|---|---|---|
| Secrets and config files | 18,263 | 72.9% | 293 | /.env and variants, /.git/config, /.aws/credentials |
| PHP, admin panels, other software | 4,886 | 19.5% | 262 | /index.php, /cgi-bin |
| WordPress | 1,328 | 5.3% | 195 | /wp-admin/install.php, /xmlrpc.php |
| Next.js and React | 345 | 1.4% | 53 | POST with Next-Action, /_rsc |
| Path traversal and injection | 209 | 0.8% | 33 | ../, /etc/passwd |
| Unusual HTTP methods | 18 | 0.1% | 7 | CONNECT |
Top of the list was /.env. It was requested 469 times from 141 addresses, and 2,156 distinct paths contained .env, /.env itself included, from /.env.local and /.env.bak to /api/.env and /backend/.env. /.git/config drew 240 requests from 118 addresses. WordPress, PHP, admin panels and other software that has never run here account for 6,214 requests, just under a quarter of the list (24.8%). A default Next.js application serves none of it.
Why a wall of 403s measures the wrong thing
Watching refusals pile up, it is tempting to call the site well defended. That reading is too generous. The site does not run the software the scanners assumed, and the files they wanted are not where they looked.
An access log records what the proxy answered. That is all. It shows nothing of what happened inside the application afterwards, and it cannot prove that a vulnerability is absent.
The requests shaped like Next.js
One family in that table stands apart. In the Next.js App Router, a server action is a function that runs on the server when, for instance, a form is submitted. The browser invokes it with a POST to the page’s own URL, carrying a Next-Action header that identifies the action and a body that holds the arguments in the React Server Components wire format. This site has exactly one server action, the contact form at /en/contact and /it/contatti.
During the window, 309 POSTs with a Next-Action header went to other paths. They came from 52 addresses, on 21 of the 28 days, and were aimed at 21 different paths, with the home page taking 185 of them and /it, /dashboard, /account and /admin following a long way behind. No request carrying the header, by any method, reached the contact form. The proxy answered 107 with a 403 and 88 with the redirect from www to the main host; 75 got a 405, 31 a 404 and 8 a 308 redirect. None got a 2xx.
What those requests carried is unknown. The access log keeps the method, path, headers and status, never the body, so it cannot tell an exploit attempt from a version check or plain noise. Their shape is known, though. According to its source code, the public React2Shell scanner that Assetnote published on GitHub sends a multipart POST to / with a Next-Action header. The requests in this log share that shape, and nothing in the log ties them to that tool or to that vulnerability.
The header alone is no signature either. Across the site’s two hostnames 4,670 requests carried it, 4,403 of them GETs, and some of those GETs were scanning for /.git/config and /.env.
The flaws that matter for this stack
React2Shell, CVE-2025-55182, is a remote code execution flaw in React Server Components, rated CVSS 10.0 and disclosed on 3 December 2025. Google’s threat intelligence group reported widespread exploitation by many threat clusters shortly afterwards, and CISA added it to its Known Exploited Vulnerabilities catalogue on 5 December. The Next.js advisory, CVE-2025-66478 (later rejected as a duplicate), lists App Router applications on Next.js 15.x and 16.x as affected and states that there is no workaround. Upgrading is the fix.
This is where the Next-Action POSTs and the real risk meet, and also where the log stops helping. For React2Shell the action ID did not need to exist. A vulnerable server decoded the body on any App Router page, which is why a scanner can aim at / with a dummy header value. The log shows the shape of these requests and nothing of their body, and a crafted request to a real page leaves a line like any other.
The second example is CVE-2025-29927, an authorization bypass in Next.js middleware. The framework used an internal header, x-middleware-subrequest, to mark its own subrequests and trusted it without checking where it came from, so on 15.x releases before 15.2.3 an application doing its authentication in middleware could be walked past by anyone who sent that header. Datadog’s analysis places the exposure in self-hosted deployments running the standalone output. Here the middleware only sets the content security policy. It guards nothing.
Neither flaw needs an unusual path. Once React2Shell was public, automated scanners probed for it within days, and a list of plugin paths says nothing about whether a site is exposed to it.
What the build does about it
The biggest family, secrets and config files, fails twice. The proxy refuses requests for secret and repository files, at the root or in subfolders, with a 403 before they reach the application, and common WordPress and PHP probe paths are refused the same way. Behind that, the files are not there to be found. Secrets reach the container as environment variables at run time, the Docker build context excludes every .env file, and nothing sensitive is written into the image. The OWASP Secrets Management Cheat Sheet covers the wider subject.
At the edge the proxy accepts only the methods the site uses, limits request size, and refuses addresses already banned by the intrusion-prevention layer. Plain-HTTP requests that name no host served here are refused too.
Against framework flaws the dependable defence is a patched release, so the effort goes into a small surface and cheap updates. The application has five runtime dependencies. The framework release pinned in the lockfile already includes the fixes for React2Shell and for the middleware bypass. A script in the repository scans the built images and the lockfile for known high and critical vulnerabilities.
The runtime image runs as a non-root user, with npm, npx and corepack deleted because the server never calls them. The container publishes no port. Only the proxy reaches it, with its Linux capabilities dropped and privilege escalation disabled. The X-Powered-By header is switched off.
Pages are served with a content security policy that carries a fresh nonce on every response, which keeps an injected inline script from running in browsers that enforce the policy. The site sets no tracking cookies. Its only cookie is a first-party language preference, written when a visitor switches language. No CAPTCHA. The contact form relies on hidden honeypot fields, a signed timestamp, a per-address rate limit and length checks on the text fields.
Nothing on that list is a product to buy. Each item is a decision taken while the site was being written.
Where this stops
The subject here is secure development and application security. Managed monitoring, a security operations centre, network defence and incident response as a service are outside that scope, and so is any continuous watch on these logs. They are examined from time to time, and the current count is a script that can be rerun on any new window. A periodic check.
Zero successful responses has a precise meaning. No request in the count got a 2xx. The access log sees nothing of attacks that do not travel over HTTP, of traffic dropped by the firewall before it reaches the proxy, or of well-formed requests with no recognisable pattern. A custom site is only as secure as the people who build it. Building one removes the large, unmaintained third-party surface that comes with a site assembled from plugins, but the framework flaws stay with it, which is why the habit that counts is updating quickly.
Reading the logs back
Most of the loud traffic hunts for leaked secrets or for software that is not installed, and a counter of refusals is a weak measure of safety. The framework-shaped requests deserve a closer look, even though the log cannot say what they carried. Moving secrets off reachable paths is a job done once. Keeping the dependency list short and the framework patched goes on for as long as the site is online.
Custralis designs, builds and secures custom websites and web applications for businesses, along the lines described above. For a project that needs this kind of care, the application security service page is the place to start.
Custralis, application security and GDPR
Sources (checked on 13 September 2026)
React2Shell (CVE-2025-55182), Google Cloud threat intelligence blog
React2Shell technical analysis and CISA KEV listing, Rapid7
Security Advisory CVE-2025-66478, Next.js
react2shell-scanner source code, Assetnote on GitHub
Next.js middleware authorization bypass (CVE-2025-29927), Datadog Security Labs