IP filtering: whitelist, blacklist, and real IP for nginx and Apache

False blocks and spoofed addresses break access to services at the worst possible moment. We set up IP whitelisting and blacklisting turnkey: we analyze real traffic sources and build perimeter validation. After implementation, the number of false blocks drops several times over, and access to your services becomes predictable.

Our competencies:

Frequently Asked Questions

Latest works

  • Development of a web application for FEEDME
    Development of a web application for FEEDME
    1365
  • Development of an online store for the company FURNORO
    Development of an online store for the company FURNORO
    1330
  • B2B Advance company logo design
    B2B Advance company logo design
    776
  • Development of a web application for Enviok
    Development of a web application for Enviok
    1077
  • AIDER company logo development
    AIDER company logo development
    1022
  • CRM development for Chasseurs
    CRM development for Chasseurs
    1113

IP whitelisting doesn't work: spoofed addresses and false blocks

A reverse proxy or load balancer in front of a web server breaks IP filtering — and this is the most common reason why IP whitelisting behaves unpredictably.

The connection to the application is established by an intermediary, so access rules end up with the address behind the proxy instead of the client: the same one for all visitors — the gateway or load balancer address.

The consequences are symmetrical and both bad. The whitelist either lets everyone through, because every request comes from one "trusted" address, or denies everyone — if the intermediary is not in the list.

The blacklist is harsher: by blocking one address, you close access to an entire pool of clients behind that load balancer, and a false block of one client turns into an outage for the whole site.

The second layer of the problem is trusting headers. The client inserts an address header manually, and if the server accepts it without verification, the spoofed IP address bypasses both the whitelist and the blacklist at once.

The rule here is one: we trust headers only from known intermediaries and parse the chain from the end — the first address belongs to the client only when all preceding ones are within trusted network ranges.

That is why IP filtering is built not on what is visible in the socket, but on the reconstructed client address.

Next we break down how this is closed: which ranges to declare trusted, in what order to read the chain with multiple hops, and how to verify the policy so that it does not let in extra traffic and does not cut off your own.

What changes after implementing perimeter filtering

While filtering lives only on the load balancer, the real client address is lost: in the logs, all requests come from the proxy address itself. Source-based rules start working blindly — legitimate users behind a shared proxy get denied, while unwanted traffic passes through.

This is fixed at the configuration level: trusted proxies are listed explicitly, and the header with the original address is accepted only from them — in nginx this is set by realip module directives, in Apache by an equivalent mechanism.

After configuration, we measure three values that show what changed at the perimeter.

  • False positive rate — requests cut off by a rule when the source is legitimate. We keep it below one percent and investigate every event: the rule is refined, not disabled entirely.
  • Access recovery time — if the list did touch an employee or contractor, the address is returned within minutes, without editing configuration or restarting the service.
  • Share of requests with correctly identified source — after configuration it trends toward full coverage. Incident investigation, rate limiting, and targeted blocks rely on this data.

The effect holds as long as the lists are alive. Counters for denials and list changes trigger an alert on a spike, and once a week the composition is reviewed: outdated contractor addresses are removed, temporary accesses are closed. Traffic source control becomes a procedure, not a one-time setup.

IP whitelisting: traffic filtering levels and their limitations

A filtering rule can be enabled at several levels: on a firewall or load balancer, on a reverse proxy, in an external filtering layer, and in the application itself.

The closer the rule is to the traffic source, the cheaper it is to cut off garbage, but the less context is available for the decision: at lower levels only the address is visible, not the domain, path, or account.

The flip side is that rules on the load balancer do not distinguish services behind one address, so different applications often need their own layer and their own trusted node list.

Level When it fits Limitations
Firewall, load balancer Need to cut traffic before it reaches the application Only the address is visible: no domain, path, or method; one list for all services
Reverse proxy (nginx, Apache) Rules depend on domain or path, real client address is needed Requires a list of trusted proxies, otherwise the address in the log is spoofed
Application code Need role, permissions, and request context, audit log accounting The request has already passed the network and queue; the rule is duplicated in every service
External filtering layer Infrastructure changes, need a unified trusted node list Dependency on an external perimeter and delay in approving rules

The levels do not replace but complement each other: the network cuts obvious garbage, the proxy brings the client address to the real one, the application makes decisions by roles.

The main limitation of any filtering is the shared address: behind one node there can be an entire office or a mobile carrier, so a trusted node list at the network level does not separate users inside. These limitations are accepted consciously, not bypassed.

Implementation order for filtering: from traffic audit to handover

The order of work matters more than the tool: an IP whitelisting rule enabled before source inventory cuts off live partners and internal services. Therefore we go from the traffic map to configuration and at every step compare the result with real requests, not assumptions.

  1. Inventory of traffic sources. We collect all entry points: external integrations, office and cloud addresses, service bots, monitoring. The output is a list of subnets marked "block", "rate limit", "leave alone".

  2. Traffic flow diagram. We break down the chain to the application: where the load balancer sits, how many nodes proxy requests, from which header the client address is read. Without this, the filter sees the proxy address and cuts either everyone or no one.

  3. Access rule configuration. We describe whitelists and blacklists, and separately close administrative sections. Trusted proxies are configured together with the rules — otherwise the original address is spoofed by the first node on the path.

  4. Dry run in logging mode. We enable rules so that denied requests are written to the log but allowed through. Over several days the full picture of false positives becomes visible — they are cleaned up before an incident, not after.

  5. Production verification. We enable live mode first on one node: we compare the share of blocked requests and service availability. Rollback is editing one configuration file and reloading.

  6. Handover to operations. We provide the diagram, configurations, the procedure for adding new addresses, and analysis of common errors. Support knows where to change the list instead of searching for it in logs during a block.

What the customer receives: configurations, diagrams, and procedures

We hand over the work not as a description of "what we did", but as a set of artifacts: they can be checked, given to another team, and updated without our involvement. Below is the package that remains with you after implementing IP whitelisting on nginx and Apache.

  • Configuration files — ready-made blocks for both servers: allow/deny rules, trusted proxies for restoring the real client address, the order in which directives are applied, and the points where new entries should be inserted. With comments on every line so that the on-call engineer can make the edit, not only the author of the scheme.
  • Access policy — which perimeters are closed by filtering, whom we allow by default, what we do with the rest of the traffic, and where network responsibility ends and application responsibility begins.
  • Request flow diagram — the path from the client through the load balancer and proxy to the application: at which node the real address is visible, where it is spoofed, and how to verify this with a single command.
  • Trusted node lists — separately for each perimeter, with the owner of the entry, date added, and a flag for whether it can be removed.
  • On-call shift instructions — scenarios for every case: add an address, remove a block, verify that the rule applied, and what to do on a false positive outside business hours.
  • Rule review procedure — review frequency, who approves an entry, how the exclusion reason is recorded, and when an address is removed automatically.

The package is designed so that the rules live without their author: a shift engineer can explain why a specific address is in the list and what will happen if it is removed.

Case study: how false client blocks were removed in one iteration

Before: rules blocked the wrong people

The customer portal sat behind a load balancer and the Cloudflare content delivery network: all requests reached the backend from perimeter addresses, not visitor addresses.

Blocking rules triggered on the perimeter address, and an entire branch of trusted client traffic was cut off instead of the source of the anomaly.

Peak hours consistently produced 6–8% denials to trusted clients, and every such case looked like "the site works, but not for me."

Incident investigation was stuck on logs: the records contained the same address, logs had to be matched against an export from the content delivery network panel, and finding the culprit took up to 40 minutes.

The share of requests with the correct original address did not exceed 12% — filtering statistics were unusable for decisions.

What was changed and what happened

The original address was restored on both fronts: in nginx — the real_ip module with an explicit list of trusted subnets and the X-Forwarded-For header; in Apache — the RemoteIP module with trusted proxies.

We validate the header chain by length and by trust in the source, otherwise a manually inserted X-Forwarded-For would bypass the filter.

The rule order was reversed in favor of the whitelist: trusted subnets and service perimeter addresses are checked before blocks, and blocks are separated by class — parsers, vulnerability scanners, anomalous request rate.

The log now records the original address, proxy chain, and client identifier — the incident is investigated from a single record.

Denials to trusted clients stopped, the share of requests with the correct address grew to 100%, and finding the culprit in logs dropped from 40 minutes to 2–3.

Before rollout, new rules are run against a copy of production logs in shadow mode — regression is visible before the client sees it.

How load balancer filtering differs from application checks

A rule on the load balancer triggers before the request reaches the application: the connection is cut at ingress, the encrypted channel is not established, workers do not consume memory and pooled connections.

This is early blocking — it is cheaper than any code check because it absorbs load at the topmost point. There is little data at this level: client address, port, node name, request rate counters.

An application check sees more — token, session, role, tenant identifier, path, and request body. But it pays the full processing cost: the request has already been accepted, parsed, and context has been raised.

Hence the order of checks: address filter at ingress, rate thresholds on the load balancer, identity decisions in code.

The main trap is rule duplication. If the same address list lives both in the load balancer configuration and in the application handler, they will diverge: an edit is made in one place, the other remains at the previous version.

Worse, the denial comes without context — the application logs simply have no request, and investigation starts with whether it existed at all.

Separately about the real address. Behind a proxy, the client address is replaced by the intermediary address, and the rule starts allowing or cutting the wrong one.

This is fixed by trusting the X-Forwarded-For header only from known intermediate nodes and explicitly parsing the chain — otherwise the address is forged with one header, and the filter stops meaning anything.

Common technical doubts when configuring access

The questions asked before starting almost always concern not rule syntax, but system behavior under load and after changes. Below are short answers that remove most doubts before work begins.

Clients arrive with a spoofed address — will the filter work incorrectly?

If traffic passes through a load balancer or delivery network, the socket shows the intermediary address, not the client.

We restore the real address using the forwarding header and the real_ip_header and set_real_ip_from directives, otherwise rules apply to the intermediate node. Then the lists work by the real address.

Can we block live clients?

The risk exists only with an abrupt switch to "deny everything except the list". Therefore we first enable logging without blocking, collect actual traffic, and build the whitelist from it. Disabling is one configuration line, without restarting the service.

We already have a proxy deployed — does the scheme change?

The node where rules are applied changes: it must be the first to accept the connection. If the filter is placed after the intermediary, it sees the same address for everyone and either blocks all traffic or blocks nothing.

How often should lists be reviewed?

By events — a new office, a partner changing provider, a service being decommissioned. Plus a scheduled review once a quarter and automatic expiration of temporary entries, so the list does not turn into a dumping ground of historical exceptions.

How do we know the filter is cutting the wrong ones?

We keep denial counters per rule and output them to monitoring: growth in the denial share on a specific path is a signal to review the entry, not wait for reports. False positives are investigated using logs with address, path, and time.

Let's discuss your filtering task and come back with a work plan

To make filtering work predictably, we start with the traffic flow diagram: where the load balancer sits, at which node encryption terminates, and at what point the real client address is lost.

On this picture it is immediately clear which headers reach nginx and Apache, where the allowlist will trigger falsely, and where the denylist will leave a bypass.

Then we define the composition of implementation stages and the scope of work. We agree on the access work plan before configuration changes — with rollout order, rollback points, and responsible people on your side.

  • Traffic flow diagram with points where the real client address is determined.
  • Rules for nginx and Apache with priorities, exceptions, and application order.
  • Trusted proxy and header configuration so the real address cannot be spoofed externally.
  • Access list update procedure and the order for adding new addresses.
  • Checks on staging and production traffic, including rule rollback.
  • Configuration documentation and handover to your support team.

Describe your filtering task: what is already configured, where proxies are located, and which addresses need to be allowed or blocked. We will come back with a work estimate and a plan where every stage has timelines and a result.