This is the third part of a small practical story about protecting ICanUp.
It started with an analysis of traffic that looked human but behaved like a bot. Then I documented the protection of Laravel sensitive paths with Nginx and Fail2ban.
The next question was the most interesting one: what would the protection show outside a controlled check, after a night of real server traffic?
I did not add anything new before this review. I simply left the server running and checked the actual Nginx and Fail2ban logs in the morning.
What I Wanted to Verify After Deployment
The controlled checks had already shown that the rules reacted to sensitive paths.
Real production traffic answers different questions:
- do these requests arrive without my own test traffic;
- does Nginx actually return the expected
403; - does Fail2ban see those events in the real log;
- do detections accumulate into real bans;
- does live traffic reveal cases that the original verification did not cover.
Real Requests for Sensitive Paths Appeared in the Logs
GET /.env -> 403GET /.git/config -> 403GET /... -> 403The logs confirmed that external clients were actually requesting /.env, /.git/config, and similar paths.
Nginx stopped those requests with 403, so they did not continue as ordinary application traffic.
The First Review Showed 13 Detections and 5 Bans
Fail2ban filter hits: 13 Total bans: 5 Test bans: 2Real bans: 3 Real IPs still banned: 3Ban duration: 24 hours At least one real banhappened overnight.At the time of the review, Fail2ban had recorded 13 filter detections and 5 bans in total.
Two bans came from my own test checks. Three came from real external traffic.
All three real addresses were still banned for 24 hours, and one of those bans happened during the night itself.
I intentionally do not publish the IP addresses. The useful part is the system behavior, not the specific addresses.
The Numbers Confirmed the Full Nginx -> Log -> Fail2ban -> Ban Chain
scanner request | vNginx matchessensitive path | v403 response | vrequest appearsin log | vFail2ban detects it |This mattered more to me than one successful manual test.
The full sequence was confirmed: Nginx recognizes the request, returns 403, the event appears in the log, Fail2ban detects it, and after the required number of detections the address is banned.
The protection therefore did not merely look correct in configuration. It operated on traffic that arrived without my involvement.
Thirteen Detections Do Not Mean Thirteen Banned Addresses
A filter hit means an event matched the pattern. A ban means a specific address satisfied the conditions for blocking.
I therefore read these metrics separately: detections show suspicious activity, while bans show how many sources actually crossed the configured threshold.
The Review Also Exposed a DEV Gap
production /.env/app/.env/.../.env blocked bysensitive-path protection DEV before the fix /.env -> blocked /app/.envDuring the additional review I found that production protection was working, while DEV still had a gap for nested paths such as /app/.env.
Such a request could reach the proxy instead of being stopped by the same protection rule.
The gap was later fixed and verified separately. I am not covering that implementation here because the important operational lesson is broader: live verification can expose differences between environments.
A Successful Result and a Discovered Gap Can Both Be True
It would be wrong to say either "the protection worked, therefore everything is perfect" or "a DEV gap existed, therefore the protection did not work."
The facts were more precise: production rules were genuinely stopping scanner requests and Fail2ban was genuinely banning sources. At the same time, the review exposed a separate DEV inconsistency.
That is exactly what makes this useful as Build in Public: not only showing a successful release, but also showing what became visible after it went live.
What the First Night Does Not Prove
These results do not mean the server is now fully protected.
- They do not prove that the application has no other vulnerabilities.
- They do not prove that every possible scanner path is already covered.
- Fail2ban is not complete DDoS protection.
- Application-level authorization and other security boundaries remain separate layers.
- One successful day does not replace continued log review.
Real Logs Added More Than Another Manual Test
controlled check known pathknown timeknown sourceknown expected result real traffic unknown sourceunknown timingreal scanner behaviorreal ban lifecycleenvironment differencesA manual test request is useful for checking one expected result.
A night of real traffic showed something different: these requests really arrive, the ban lifecycle starts without my involvement, and different environments may not behave identically.
For security changes I therefore treat post-release observation as its own verification stage.
For Build in Public, the Day After Release Matters Too
It is easy to publish an article at the moment a feature is finished and every controlled check is green.
It is more useful to return after real operation and answer a second question: what happened next?
In this case the answer is valuable precisely because it is not idealized: the protection genuinely worked, real bans occurred, and the review still found another gap that the original scenario had not exposed.
What I Took Away from the First Night
My conclusions from the morning review:
- Scanner traffic was not theoretical. Requests for sensitive paths arrived in real traffic.
- Nginx protection acted before the application layer. Known sensitive paths showed
403in the logs. - Fail2ban worked on the real event stream. There was a real overnight ban, not only test bans.
- Filter hits and bans should be read as separate metrics.
- Every environment needs verification. The nested-path DEV gap exposed a separate problem.
- Security deployment should be followed by observation. The real logs provided the most useful feedback.
Real Traffic Confirmed the Protection and Challenged It at the Same Time
After the first night I got exactly the kind of result I wanted from leaving the protection alone under real traffic.
Nginx genuinely returned 403 for scanner requests. Fail2ban genuinely accumulated detections and banned addresses. At the time of review there were 13 filter hits, 5 total bans, 2 test bans, and 3 real bans.
At the same time, the analysis exposed a DEV gap for nested sensitive paths that was later fixed and verified.
For me that is stronger evidence than "the test passed": the system proved useful under real traffic and immediately produced the next concrete signal for improvement.
A security change becomes much more informative after deployment: real traffic shows what actually works and what still escaped the original checks.



