Understanding iptables: What You Really Need to Master is Not Rules, But the Flow of Packets
本文最后更新于 268 天前,其中的信息可能已经有所发展或是发生改变,如有失效可到评论区留言。
Article Abstract
Through a real-world case of a VPS being attacked, this article reveals the limitations of traditional iptables tutorials, pointing out that simply memorizing rules cannot address practical issues. The core lies in understanding the complete path of data packets in the kernel: the flow logic from PREROUTING to FORWARD and then to INPUT, as well as the coordination mechanism of systems like Docker and Kubernetes inserting rules into different chains. The author emphasizes that correct configuration must be based on an understanding of the traffic path rather than mechanically piling up commands. By analyzing real incidents (such as Docker port mapping bypassing the INPUT chain) and system interaction principles, precise control over firewall policies is ultimately achieved, avoiding security vulnerabilities caused by incorrect rule placement.
Qwen3-14B · 2026-06-18

1 Introduction

Actually, I have wanted to write an article about iptables for a long time, because although I don't usually use it, every time I actually need to use it, I always find that I have completely forgotten the relevant knowledge points and have to review them all over again from scratch. This is the tragic consequence of learning a certain knowledge point without reinforcing the memory through writing.

But why have I always wanted to write this, yet could never make up my mind to start? The reason is simple: articles about iptables are really hard to write. It's so easy to end up writing the kind of “manual-style” tutorial found all over the internet, piling up chains, tables, and rules, leaving readers in a fog while reading and completely confused afterward—that was me before, getting lost in all the explanations.

So the tragedy happened—when I first bought my Racknerd Chicago VPS, I had planned my security strategy very clearly: only open two high-range ports (SSH with key login and Transmission), drop all inbound requests, and manage all internal services via Tailscale IP. It looked rock-solid. Therefore, I used the simplest username and password (wordpress/wordpress) for my MariaDB database. After all, the VPS didn't expose port 3306 to the outside world, so anything should be safe. But the very next day, the WordPress database in MariaDB was deleted. I was utterly humiliated~~~.

After troubleshooting, I found the culprit was an inconspicuous Docker creation parameter: -p 3306:3306. Just a casual keystroke like that directly exposed the database to the entire world. At that moment, I felt devastated: I had locked down the system-level firewall so tightly, so why could Docker bypass it so easily?

Only then did I truly realize—I thought I understood iptables, but in reality, I only knew some “commands.” I had absolutely no understanding of how traffic actually flows, why it behaves that way, and what the system and Docker were quietly doing behind the scenes.

This is exactly the biggest problem with traditional iptables tutorials: they tell you what tables and chains exist and what various rules mean, but they don't tell you the order in which things happen, nor do they tell you that what truly affects actual behavior is the “traffic path.”

For example: Where does a packet enter from? Which chain does it go through first? Which rules are built into the system? Which ones are sneakily inserted by Docker? Why did the DROP rule I painstakingly wrote never even get a chance to take effect?

Traditional tutorials assume you will “figure these things out on your own,” but the reality is that most people simply don't, just like me back then—Docker automatically added a DNAT rule on the host machine to forward port 3306 from the external network, paired with an ACCEPT rule, completely bypassing my input firewall. My originally confident, “impenetrable” security plan actually never even ran before the rules inserted by Docker.

This incident made me completely realize: iptables is not a “command tool” at all, but a “traffic routing map.” What really matters is not what rules you write, but the exact order in which packets pass through these chains. If you don't understand this path, you can never truly control the firewall.

So for this article, I want to switch to a writing style that is more suitable for ordinary people and better fits my own brain's logic: starting from real incidents and real needs, clearly explaining the traffic path of iptables, so that people can treat it as a map in their minds rather than an instruction manual piled on a desk.

I hope that after reading it, you will have an “Aha! No wonder I always felt I only half-understood it before” feeling. And for myself, this article is also a way to “completely lock this knowledge into long-term memory,” preventing another incident of relying on luck for firewall security.

2 Why are traditional iptables tutorials so hard to understand?

If you have looked for iptables tutorials online, you probably have had the same experience: the articles are usually written in great detail, explaining the four tables, five chains, and every parameter very clearly. The content seems quite substantial, but after reading, your head is still a complete mess. You remember the names of INPUT, FORWARD, and OUTPUT, you know what PREROUTING is, and you vaguely know what NAT, Filter, and Mangle are responsible for, but when it actually comes to configuring a firewall, you still start getting confused at the very first line.

You will ask yourself: Why did I write a DROP rule, but the packet wasn't dropped? Why did I think INPUT was the first line of defense, yet Docker seems to be able to open a door directly from behind? Why did I type commands exactly as the tutorial said, but the behavior was completely different from what the tutorial explained? I was just as confused back then.

The problem is not that you are not smart enough, nor that you are not working hard enough. It is that most iptables tutorials “teach it backward” right from the starting point. They will first tell you that “there are four tables in the world,” then tell you “which chains are in each table,” and then go on and on about parameters and syntax, making you feel as if you have mastered most of it. But in reality, this content is just a bunch of nouns in a fixed structure; they cannot explain why the system does a certain thing, nor can they help you understand how real-world network traffic actually flows.

This is like someone teaching you how to drive by very patiently telling you what the steering wheel, accelerator, and brakes are, but completely failing to tell you how the roads are planned, how traffic flows, which intersections have the right of way, and which sections are one-way streets. You know all the parts of the vehicle, but you still don't know how to drive because you don't know the actual behavioral logic of cars on the road.

This is exactly the biggest problem with iptables: it is not a “collection of commands,” but a “traffic routing system.” It processes packets, not commands. Packets, like cars, enter from an entrance, pass through several “main roads,” get turned into a “side road,” might be inspected, might be released, or might have their destination changed. Your rules are just signs placed at these intersections. If you don't know the order of the intersections, you will never know if your sign is placed somewhere where no one will ever pass.

Traditional tutorials often avoid talking about this. They tell you that INPUT is where inbound packets are intercepted, so you naturally treat INPUT as the “first gate.” However, in a real system, before a packet reaches INPUT, it has already passed through other places, such as PREROUTING, and Docker quietly inserts its own jump rules in these “places you don't know about.” By the time you see INPUT, everything has already been settled.

What's worse, most tutorials won't tell you at all that the system itself dynamically generates some rules. This means you think the DROP rule you wrote is at the “top level,” but in reality, the system has already inserted several ACCEPT rules ahead of you, placing your painstakingly written rule in a corner where no one passes. You think you wrote it wrong, but in fact, you were standing in the wrong position right from the very first step.

This is also why, even if you know what tables are, what chains are, and what -A and -I are, you still find yourself completely unable to predict the actual behavior of iptables—because you were never told how the entire world operates.

So rather than saying traditional tutorials are hard to learn, it's better to say they don't enter this knowledge system from the “correct entry point” at all. The world of iptables must be understood starting from the “path of traffic,” from “how packets travel through the system,” rather than from a pile of nouns. Nouns have no meaning; paths do. If the order is not clear, rules will always be nothing but empty talk.

In this article, I hope to bring the discussion back to the right direction. Not by explaining concepts, not by memorizing commands, but by first building that “traffic map” you must have in your mind. Only by understanding that map can you truly understand iptables, understand why Docker can bypass INPUT, understand why the rules you write fail, and understand why incidents happen.

Starting from this chapter, what we are going to do is blow away all the “fog,” letting you see the full picture of iptables for the first time, rather than a pile of parts scattered on the desk.

3 The “path” of data packets in the kernel”


Before officially starting the content of this chapter, there is one thing that must be emphasized, because if this is not understood, all the chains, tables, and hook points that follow will be completely confusing, and that is:iptables is actually not responsible for “processing” any packets.

It is more like an “administrator's remote control”: you type a few commands with it, and it writes the rules into the kernel; the one that actually blocks packets, allows packets, does NAT, and does connection tracking is actually inside the Linux kernel netfilter doing the work.

So, whether you are using iptables, nftables, or firewalld, it is essentially:User space (you) → Send commands → Kernel space (netfilter) → Kernel runs the actual traffic processing logic。

Those INPUT, FORWARD, PREROUTING, and POSTROUTING you see are not actually the “workflow” of iptables, but rather several key nodes of netfilter in the kernel. iptables just stuffs rules into these nodes.

In fact, what truly gets people confused by iptables is never that they didn't memorize the commands, but that they don't have a clear roadmap of “how packets move in the kernel” in their minds. You don't know when it will pause, at which hook it will be processed, when it will bypass your rules, or when it will be intercepted by mechanisms like Docker, NAT, or FORWARD. So you understand the commands, but you don't understand what Linux is actually doing to your traffic.

I was also bitten by Docker's DNAT on my VPS because of this: I thought an external request would definitely “go into INPUT first” before having a chance to be mapped by Docker; but Linux actually goes to PREROUTING (nat table)first, and Docker quietly inserted DNAT right here. In other words, Docker's modification happens before all of my INPUT rules—which is completely different from the workflow in my head.

This is why “understanding the path” is far more important than “understanding the commands.” Because commands can be forgotten, but the route does not change; once the route is clear, all chains, tables, and rules make perfect sense.


If you could only choose one diagram to understand at a glance how Linux processes a packet in the kernel, and where the links of iptables, netfilter, NAT, and Docker are actually inserted, it would be the one you see now—it shows the workflow of the entire ”Linux kernel network stack,” except we will use iptables chains as reference coordinates to explain it:

image.png

Next, we will start with this diagram, letting it guide you to track the packet all the way—from the moment it enters the network card, to being received by the local machine, forwarded by a container, or sent back to the network. You will find that once you understand its route, iptables, netfilter, NAT, Docker, and even the subsequent nftables, are no longer mysterious.


From NIC to PREROUTING: The first stop, the entry point for everything

All incoming access, whether it is a webpage you are visiting or an attacker scanning your ports, first enters the network card and then goes straight to the left side of the diagram: PREROUTING。

Here, it will pass through in order: raw, mangle, nat (the most critical one here). You can think of PREROUTING as the “real first gate”. Did you think INPUT was the first stop of the firewall? That is a common misconception.

The real first stop is PREROUTING. A lot of network dark magic happens here—especially DNAT (port mapping). My incident that day was because Docker added this here:

DNAT --dport 3306 → 容器内部端口

So when attackers scanned port 3306, they didn't even reach my carefully written INPUT chain; they were already led into the container by Docker at PREROUTING.

The significance of this section is:If you don't understand PREROUTING, you will never be able to explain why no matter how beautifully the INPUT chain is configured, it is useless.


Is the Destination the local machine? Or is it to be forwarded? This small section in the diagram is the major fork in the road for the entire iptables.

In the diagram, there is a red diamond that says: “Destination is local machine”. This is the dividing line for all routing behavior: if the IP given is your VPS's IP → Y → enter INPUT; if it is not your local machine → N → go to FORWARD.

Such a simple judgment, yet it causes many people to never understand: Why doesn't Docker traffic enter INPUT? Why does Kubernetes' Service NAT go through FORWARD? Why can port mapping on a VPS bypass your INPUT? Why do packets from virtual bridges like br0 and docker0 all go to FORWARD?

If you only have literal understandings like “INPUT = inbound” and “OUTPUT = outbound” in your mind, you will never truly understand these questions.

But once you see this diagram, you immediately know: Docker networking is not “accessing my host”, it is accessing the internal network forwarded by the docker0 bridge, so it naturally goes through FORWARD.


Taking the Y branch: entering the real “local firewall”—INPUT

If the packet is indeed destined for your VPS's IP, it will go towards the INPUT at the top of the diagram.

In INPUT, it will pass through in order: mangle (usually ignored), filter (where most people write DROP). This section is the “VPS firewall rule zone” in the traditional sense.

If you want to block a certain port, restrict a certain IP, or ban a certain protocol, this is where you do it. But now it is very clear that:INPUT is the second gate, not the first.

Therefore, the prerequisite for what it can filter is:It must not have had its “destination address changed” in PREROUTING,This is exactly why I fell into the trap before.

After passing through INPUT, the packet has already entered the kernel application layer, and further up is the process itself, such as sshd, nginx, or docker-proxy.


Taking the N branch: Not destined for me? Then forward it—the FORWARD chain comes into play.

If the destination address is not your local IP, the packet will enter the FORWARD on the right side of the diagram.

Here too: mangle, filter

Docker, container networking, Kubernetes, router forwarding, bypass routing... all of these go through this section.

This is also why Docker inserts DOCKER and DOCKER-USER into FORWARD, and it inserts them at the very beginning of FORWARD, giving them mandatory priority.

When you understand this diagram, you will immediately realize:Any container traffic (including ‘-p 3306:3306’) will go through the PREROUTING and FORWARD written by Docker beforehand, and will not go through INPUT.

This is the fundamental reason why “you cannot protect containers using INPUT”.


Packets going out from the local machine: OUTPUT → POSTROUTING

If your program sends a request to the public network, it will go down from the output path at the top of the diagram. The flow is: output (raw → mangle → nat → filter) → POSTROUTING (mangle → nat) → NIC → egress.

This section is usually relatively quiet because most people do not set rules in OUTPUT. But if you want to understand SNAT / MASQUERADE, this is where it happens.

A short phrase to remember is:DNAT is first (PREROUTING), SNAT is last (POSTROUTING).

When Docker's bridged network forwards packets out, it performs MASQUERADE here in POSTROUTING.


After going through the diagram, you will truly understand iptables.

This diagram is actually the “anatomy chart” of iptables. No matter how complex a command is written, as long as you can answer: in which section of the diagram is it executed? Then you will never get lost.

Now you also know:

  • INPUT is indeed not the first stop.
  • PREROUTING is the critical path that affects Docker, port mapping, and reverse proxies.
  • FORWARD is the main battlefield for all “bridges/containers/routing forwarding”.
  • POSTROUTING is the inevitable path for SNAT.
  • OUTPUT is only traversed by packets sent by local programs.
  • The “dichotomy” between local and non-local directly determines the diversion of INPUT and FORWARD.

Summarizing this chapter in one sentence:Don't memorize iptables; just go through this diagram once, and you'll get it.

4 The so-called “4 tables and 5 chains” are actually functional areas on the roadmap

The place where most people get stuck when learning iptables is not the commands, but that pile of head-spinning terms: things like the filter table, nat table, mangle table, raw table, and chains like INPUT, OUTPUT, FORWARD, PREROUTING, and POSTROUTING. Most tutorials like to throw all these terms at you right from the start and say “iptables is made of these”. But the problem is, when you don't have a roadmap of “how packets travel in the kernel” in your head, these names are like chapter titles in an unfamiliar reference book, completely lacking intuitive meaning, and only making you more confused the more you look at them.

To truly understand them, you must first realize a very critical, but often overlooked fact:Chains do not exist independently; a chain always belongs to a table. What you see is not an abstract “PREROUTING chain”, but PREROUTING in the nat table, PREROUTING in the mangle table, and PREROUTING in the raw table.


It is worth emphasizing that,The so-called “4 tables and 5 chains” is just the default starting point of iptables, not its complete form —They are more like a pre-drawn skeleton of a roadmap, telling you roughly which nodes packets will pass through in the kernel, but it does not mean these nodes only contain the rules you wrote. In fact, iptables tables and chains themselves are extensible structures, and both the system and applications can continue to subdivide logic, insert jumps, and build their own processing flows under these default locations.

Therefore, when you actually look at a running server, what you see is often not a “clean 4 tables and 5 chains”, but a real traffic path map that has been continuously supplemented, split, and extended. The significance of understanding this is not to memorize which extra chains there are, but to build a correct intuition:iptables describes not a static rule table, but a traffic processing framework that can be continuously edited.


Understanding this is very important because it helps you build the correct intuition right from the start: iptables is not a static form, nor is it a rigid conceptual system, but a “traffic routing map” that is jointly edited by the system and applications. What you need to read and understand are not nouns, but the actual path of the packets; not to memorize tables and chains, but to understand who should take over and what operations should be executed at different nodes.

If you imagine the Linux kernel processing traffic as a government building with a very detailed division of labor, it will be much easier to understand. There are four halls (four tables) in the building: raw is responsible for the most primitive registration, mangle is responsible for modifying metadata, nat is responsible for address changes, and filter is responsible for final release approval; each hall has some counters (chains) set up to handle “where the traffic comes from and which counter it needs to go through”. Rules are the specific actions you stick on the counters, like “if X happens, execute Y”.

And a point that is easier to overlook is:The chains contained in each table are not the same; they are not symmetrical.

For example, the raw table only has PREROUTING and OUTPUT, because the most primitive registration only happens the moment traffic enters the building, and when traffic is sent from the local machine internally; the nat table naturally needs to set up counters in three places: PREROUTING (DNAT), OUTPUT (DNAT for traffic initiated by the local machine), and POSTROUTING (SNAT); the filter table only cares about INPUT, FORWARD, and OUTPUT, because “whether to allow passage” only needs to be judged in these three scenarios; while the mangle table, because it modifies metadata, is used in a very wide range of scenarios and exists in almost all five chains:

image.png

If you view each table as a different business department and each chain as a counter in the hall, this distribution is no longer abstract, but logical. More importantly:There is also an order among these four tables, and the priority is hardcoded. raw → mangle → nat → filter is the order in which Linux processes a packet: it must first be registered (raw), then have its metadata modified (mangle), then have its address processed (nat), and finally decide whether to allow passage (filter).

You want to do DNAT in filter? You can't; you want to skip connection tracking in nat? That's raw's job; you want to make the final release decision in mangle? That belongs to filter.

The sequence of the flow determines where rules can be placed and whether they will take effect. This is why understanding the “relationship between tables and chains” is more important than memorizing commands.

Once you get these prerequisite concepts straight and look back at that traffic roadmap, you will find that “4 tables and 5 chains” does not need to be memorized at all. They are naturally distributed according to the route packets travel in the system, rather than being terms artificially invented by textbooks. As long as the route is clear, what each chain is and what each table is responsible for will naturally form an intuition, just like you know where the entrance, exit, and toll booth are even without getting on the highway.

Once you understand it this way, iptables is no longer an “unmemorable system of terminology”, but a transportation system that fits real-world logic: traffic will pass through different tables and different chains according to the route, and you are just pasting the rules you want at each intersection.

5 How iptables works in coordination with other systems


In Chapter 4, we deliberately shifted our attention away from the “noun list of 4 tables and 5 chains” and only established askeleton of a roadmap of how packets travel in the kernel. But this roadmap is not just for you to see—any system running on this server, as long as it needs to process network traffic, will leave its own mark on this map。

And what this chapter is going to discuss is exactly where these “marks” come from, and how they change the actual path of traffic without your knowledge.


When you actually run the server, you will find that iptables is not an “independent tool” at all—it is more like a public transportation hub. Any system that needs to process network traffic—Docker, Podman, Kubernetes, Tailscale, or even HAProxy, FRP, Clash—will cut in line, insert rules, grab priorities, and change paths on Linux's kernel network stack. They won't consult with you; they will directly write their rules into various key nodes of netfilter. If you are not careful, incidents like a service that was working fine yesterday being inaccessible today, or a database being mysteriously exposed, will occur.

The key point of this chapter is: you must view iptables as a “public hub”, not your personal firewall. All systems are using it at the same time, and you are just one of them.

The most typical example is Docker. As long as you use -p to map ports, Docker will write DNAT in PREROUTING to map your public port to the container; then add an ACCEPT rule in the FORWARD chain so that packets can continue to be forwarded; and finally add MASQUERADE at the POSTROUTING position so that the container's return packets can go out correctly. You didn't write a single line of iptables in the entire process, but Docker has silently done all the work of a router for you. So what you actually see is: your INPUT chain has nothing allowed, yet Docker's container is exposed to the public network. This is not a bug, but the natural result of Docker and you “sharing the same security system”.

Tailscale follows a similar logic; it takes over the routing table in the kernel and also rewrites iptables rules to make packets passing through its virtual network interface (tailscale0) take specific paths; it even automatically handles NAT and reverse traffic forwarding. If you use Tailscale Funnel or Tailscale Serve, it will also establish temporary ports on the local machine and direct traffic from the user-space proxy back to the kernel. These steps are transparent to you, but they mean Tailscale will insert its own rules into the routing and forwarding stages, affecting the overall traffic path of your machine.

Look at Kubernetes again; iptables in a K8s environment is the most terrifying and best illustrates the concept of “working in synergy”. Kubernetes automatically generates a large number of chains on each node, such as KUBE-SERVICES, KUBE-PORTALS, KUBE-MARK-DROP, etc., implementing ClusterIP mapping of services, SNAT of Pods, and load balancing paths of Services entirely with iptables. If you have just dozens of services, the iptables chains will automatically become extremely long, and every packet must pass through these chains. In such a system, your custom rules simply cannot be written directly in the main chains; instead, they must “bypass” the entire K8s process, otherwise, at best they won't work, and at worst they will directly cut off the service traffic.

Then there are HAProxy and FRP. These two tools do not write iptables rules themselves, but they are often tightly bound to your firewall. For example, if you want HAProxy to only listen on an internal IP and have external access go through a certain jump server, you must perform DNAT in PREROUTING so that external packets are forwarded to HAProxy by the local machine after coming in. If you don't understand this detail, you will think that “starting the proxy makes it work,” when in fact the traffic doesn't reach the location you think it does at all. Many people who encounter issues where HAProxy cannot forward traffic at all are tripped up by this pitfall of “no coordination between iptables and services.”

You can also see some more “invisible” collaborators, such as OpenVPN, WireGuard, Clash, and Surge. To perform traffic hijacking or transparent proxying, these tools write to the mangle table, mark packets, modify routing, and direct traffic to a user-space proxy before sending it back to the kernel. Instead of modifying filter rules, they change routing decisions. If you don't understand what they have inserted into the mangle table or routing table, you might think your INPUT rules are written correctly, only to find that the traffic is “intercepted” by the user-space proxy, loops around, and comes back, which is completely different from what you expected.

All of these systems together constitute a reality: any slightly complex server environment is using iptables simultaneously. You are not the only “firewall administrator.” You are just one of many users.

Only after understanding this can you truly take control of your system. Because you know: your rules must be written in the correct place; you know Docker will definitely insert into PREROUTING; you know Tailscale will hijack routing; you know Kubernetes will make FORWARD extremely complex; you know proxies and VPNs will touch the mangle table and routing table; and you know some systems insert themselves at a higher priority than you, and relying on INPUT alone simply won't stop them.

From this perspective, iptables itself is not complex. What is complex is “who is using it along with you.” When you know which systems modify which nodes of netfilter, you can design truly reliable and controllable security policies, and no longer be overwhelmed by mysterious traffic behavior.

The focus of this chapter is not to list which chains each system writes, but to make you understand: in the modern Linux ecosystem, iptables is not a “standalone tool,” but a shared platform. What you really need to manage is the collective modifications made by all systems to the kernel network stack, rather than looking at any single chain in isolation.

6 Starting from real needs, writing iptables rules that won't cause trouble

1 Using a real VPS scenario to string “tables, chains, and rules” into a truly usable solution

Before you actually start writing iptables rules, there is a question that is often overlooked: what gets most people stuck is not how to write the commands, but not knowing where to start. This is because many tutorials jump straight into tables, chains, and parameters, but rarely start from a more realistic perspective—on a real server, which traffic do you actually want to allow through, and which do you want to block? So in this section, we won't touch commands yet, but will start with a very common VPS usage scenario.

Suppose you now have an ordinary Linux VPS with a straightforward purpose: it provides Web services to the public network, requiring ports 80 and 443 to be open; you log in via SSH through a non-default port (such as 54331); at the same time, Docker is running on this machine, with one of the containers listening on 3306 for database services (this 3306 is not a service for public network users to access; it is only for other applications on the local machine, or containers on the same machine, to access the database through the host network. In other words, it “exists,” but that doesn't mean you want it “open to the outside”). Your security goal is also very clear: only allow necessary inbound access, and reject all other TCP, UDP, and ICMP requests from the outside. If you condense this requirement into one sentence, it is actually very simple: let in what should come in, and keep out what shouldn't.

The security requirements are summarized in the table below:

Requirement Type Scenario Description Allowed? Remarks/Notes
Web Service Public network access to HTTP (80) Allow Provide website services to the outside
Web Service Public network access to HTTPS (443) Allow Provide website services to the outside
Management Access SSH connection (port 54331) Allow Log in to the server using a non-default port
Database service Access host 3306 (Docker container) Allow (local/intranet only) Not a public service, only accessible by local applications or containers
Other TCP inbound traffic Any source, any port Reject TCP requests not in the explicit allow list
Other UDP inbound traffic Any source, any port Reject Including various probes and abnormal traffic
ICMP inbound traffic ping / traceroute, etc. Reject Do not expose ICMP to the public network
Forwarded traffic VPS acting as a router or gateway Not applicable The host does not act as a forwarder
Outbound traffic Host actively accessing the outside Allow Outbound connections are not restricted by default

When you look at the problem from this perspective, you will find that the thinking path of iptables is actually not abstract. Because almost all of what you currently care about is the traffic entering this machine from the outside.

After an external packet arrives at the VPS, there are usually only a few possibilities: it is either sent to a service on the host itself; forwarded to another host; or dropped directly at some stage. In this scenario, this VPS does not act as a router or gateway, nor does it need to forward traffic. This step of judgment is very important, as it immediately helps you rule out the FORWARD chain branch.

Looking back at the “5 default chains” now, their respective roles have actually begun to differentiate. PREROUTING occurs just as the packet enters the kernel, before its destination is decided, and is more related to address rewriting and early processing; POSTROUTING occurs when traffic is about to leave the host, mainly used for source address translation; OUTPUT is for connections initiated by the host itself; FORWARD is used to forward traffic, which can be clearly ruled out in this scenario. In this way, the only thing that really needs attention is the gateway that external traffic must pass through before entering the host.

Returning to the intuition established earlier: the filter table is responsible for access control; the nat table is responsible for address translation; mangle and raw are used for lower-level or more special processing. What you need to do now is essentially only one thing—decide which traffic can enter this machine and which cannot. Therefore, the core position of this entire set of rules is actually very clear: right in the INPUT chain of the filter table.

Note that this conclusion is not derived from rote memorization of “4 tables and 5 chains”, but is naturally deduced step-by-step from the requirements.

Breaking it down further, you will find that all inbound traffic can actually be clearly divided into two categories: one that you explicitly want to allow, such as Web services and SSH; and the other, all other requests that you have no reason to allow. This happens to correspond to a very classic and secure firewall writing approach: first write down clearly “what is allowed”, and finally use a catch-all rule to drop everything else. As for Docker, it is not an exception to this model. The reason you don't need to manually write complex rules for 3306 is not because Docker bypasses iptables, but because Docker will actively insert its own chains and jump rules into the existing rule system to handle “access between containers and inside the host” without affecting your overall control of public inbound traffic.

Up to this point, we still haven't written a single command. But you have already completed the most critical step: transforming an abstract security requirement into a clear “traffic decision roadmap”. If you vaguely feel at this moment that iptables is starting to change from a bunch of nouns into a system that can be reasoned and planned, then this section has achieved its intended purpose.


It should be noted that the focus of this article is not to exhaustively list the configuration methods of iptables in all scenarios, but to use one of the most common and easiest-to-implement VPS usage scenarios to clearly explain “how to think about and plan rules” 。

Therefore, the examples mainly revolve around inbound access control, and the rules are naturally concentrated on the INPUT chain of the filter table. In other usage scenarios, such as using Linux as an independent firewall, gateway, or forwarding router, chains like FORWARD, PREROUTING, and POSTROUTING will inevitably be involved, and the reliance on nat and mangle tables will be even more pronounced.

However, no matter how the roles change, the underlying way of thinking is exactly the same: first clarify where the traffic comes from and where it is going, then find the gateway it must pass through on the kernel “roadmap”, and finally decide whether to allow, modify, or drop it at that position.


2 Starting from rules: translating your thoughts into actual iptables configurations

Before actually writing the rules, we have already figured out one thing in 5.1: the core of this VPS's security strategy iscontrolling who can actively connect inTherefore, all key rules should revolve around the INPUT chain of the filter table.

When writing the INPUT chain, there is a consensus that requires almost no debate:Connections that have already been established or related must be unconditionally allowed.

If you can't even do this well, the firewall configuration itself will become an act of “self-harm”.

iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

The meaning of this rule is very simple: as long as it is communication already approved by the current server, whether it is SSH, HTTP, or return traffic from container outbound requests, it will not be reviewed repeatedly.

On this basis, what to do next is much clearer:Explicitly write down the service ports you want to open to the public network.

For a typical Web VPS, this list is often very short.

iptables -A INPUT -p tcp --dport 80  -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT

The existence of 80 and 443 needs no extra explanation; they are the main reasons why this server is “accessed”. As long as you are running a website, these two rules should be the most prominent part of the INPUT chain.

Next is the management channel, assuming port 54331 is used as the SSH login port:

iptables -A INPUT -p tcp --dport 54331 -j ACCEPT

Note: Changing SSH from 22 to a non-standard port does not bring real security, but it can at least significantly reduce meaningless scans and log noise. At the rule level, its status remains very clear:This is an entry point whose purpose you are clear about and are willing to take risks for.


At this point, it is necessary to make a special note about Docker-related ports. For example, the database container listens on 3306. This port does exist, but it is not a “public service”. It only needs to be reachable within the container network or the host itself, and Docker itself has already handled this part of the traffic through NAT and the FORWARD chain. Therefore,you do not need, and should not, write ports like 3306 into the allow rules of the INPUT chain.


Once all the “entry points you explicitly want” have been clearly written down, the remaining traffic in the INPUT chain consists of only one category: requests you have no reason to allow. At this point, the strategy becomes extremely simple:

iptables -A INPUT -j DROP

This rule is not “cold and ruthless”, but rather a natural summary of all the preceding rules:All inbound connections outside the allowed list are rejected without exception.

Finally, putting the above rules together in order, you will get a very clean and highly readable INPUT chain policy:

iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

iptables -A INPUT -p tcp --dport 80    -j ACCEPT
iptables -A INPUT -p tcp --dport 443   -j ACCEPT
iptables -A INPUT -p tcp --dport 54331 -j ACCEPT

iptables -A INPUT -j DROP

If you can clearly explainthe reason for the existence of each rule,then this firewall configuration is healthy; if there is a rule where you can no longer explain what it is “protecting”, then it most likely shouldn't exist.

3 Docker, port exposure, and iptables: the boundary of responsibility behind a real incident

In the previous examples, we deliberately treated the database service as a component that “exists, but is not open to the public network.” This is easy to understand on paper, but in a real environment, it is precisely the place most prone to misoperation and accidents. The problem does not lie in the iptables command itself, but often occurs at an earlier step:How container ports are exposed。

Many Docker tutorials, when demonstrating database containers, will directly use a command like this:

docker run -p 3306:3306 

This command is syntactically perfectly correct and works fine, but it implies a very crucial, yet often overlooked fact:It will make the database port listen on all network interfaces of the host machine. In other words, this 3306 is visible not only to the local machine but also to the external network. If the host is in an internal network, it doesn't matter much, but if the host has a public IP address (or port mapping with a public IP), then things get bad!

If you at this point take it for granted that “it's fine, I have iptables, and I will control it in the INPUT chain,” then the risk has actually quietly emerged.

In the real world, Docker does not simply hand ports over to the INPUT chain for processing. It inserts its own chains and jump rules into the nat and filter tables, changing the path of traffic entering the container. If you do not understand how these rules are inserted, it is easy to end up with a consequence:The rules appear to be written, but in reality, they do not block what you thought they were blocking.

The real accident mentioned earlier in this article—where a WordPress database was directly accessed and deleted—essentially happened this way: the database container port was unintentionally exposed to the public network, and the firewall rules did not cover the path that was actually in effect.

But when you change the port mapping method to the following, the nature of the problem changes fundamentally (here, 100.x.y.z is not a public address, but a virtual internal IP assigned by Tailscale):

docker run -p 100.x.y.z:3306:3306 ...

The real meaning of this command is—Only listen on 3306 on the Tailscale private network interface,taking my Chicago VPS as an example, the actual effect is only mapping 3306 on the Tailscale IP 100.x.y.z to 3306 on the container IP 172.19.0.4:

image.png

In other words, before the traffic even enters the decision-making system of iptables, you have already completely removed the database from the “public network world.” To public network scanners, this port does not even “exist.”

Understanding this is very important because it clearly delineates the boundaries of responsibility at several different levels: the INPUT chain of iptables is responsible for “which traffic can enter this host”; Docker's port binding determines “whether this service is exposed on a public network interface”; and the DOCKER-USER chain automatically created by Docker is an optional insurance policy provided when you indeed need fine-grained control over container traffic.

When you have already restricted the attack surface to the internal network during the port binding phase, the iptables rules are doing more of defense-in-depth; but once you expose the port to 0.0.0.0 at the very beginning, the firewall rules will be forced to bear the responsibility of a risk safety net that does not belong to them.

Returning to the core intuition emphasized throughout this article: iptables is not an “all-powerful shield.” It is more like a traffic dispatch system rather than a security patch to make up for your front-end design mistakes.

If you can truly realize in this section that:“Whether 3306 is open to the outside” is often earlier and more critical than “what rules are written in the INPUT chain”,then your understanding of iptables has already far exceeded the level of “knowing how to write a few commands.”

7 The performance and scalability principles of iptables

When a person truly begins to use iptables to manage servers, they often go through these stages: at first, they think rules are executed “as many as you write”; later, they find that Docker, NAT, and conntrack create a lot of performance overhead they weren't aware of; and only later do they realize—the performance bottleneck of iptables is not the number of rules at all, but rather which “heavy lifting” in the kernel the rules you wrote actually trigger.

What many people don't know is that as long as you use NAT, and as long as you write most filter rules, the Linux kernel will basically introduce conntrack. This “connection tracking system” is essentially a hash table used to remember the 5-tuple and state of each connection, allowing NAT to perform return packet translation normally and enabling rules like “ESTABLISHED,RELATED” to work. The benefits are obvious, but the downside is that in high-concurrency scenarios, conntrack itself becomes a huge performance bottleneck: when there are many connections, the kernel constantly looks up the table; when there is a lot of NAT, even more states are stuffed into the table; when hash collisions increase, it starts to slow down; and when the table is full, it might even drop packets directly. This is also why NAT on some VPSs suddenly fails under high traffic—it's not that iptables is broken, but that conntrack has blown up.

The performance loss of NAT is actually much larger than most filter rules. This is because NAT is not just about simply changing an IP or port; it needs to create mappings, synchronize states, maintain timers, and manage return packet paths for each connection, making the entire overhead “heavyweight.” If you run even a few more containers, and Docker/Kubernetes uses it to manage a large number of port mappings and traffic forwarding, the growth rate of conntrack will be more exaggerated than you think. This is also why many production environments either disable conntrack, minimize NAT as much as possible, or switch to bypass methods like ipvs.

Speaking of performance overhead, many people's first reaction is “more rules make it slower.” This statement is not entirely correct, at least not for all chains. For example, the INPUT chain has the greatest impact on local services, but generally does not have much traffic; PREROUTING and POSTROUTING are the busiest chains during peak traffic because all inbound and outbound packets pass through here. If these locations are hung with complex rules, the server's latency will spread all the way from TTFB to the overall response time. So whether there are many rules is not important; what is more critical is where you write the rules and whether they will affect the kernel's “busiest nodes.”

In the packet processing logic of the Linux kernel, there are actually “fast paths” and “slow paths.” When conntrack has already established a mapping, or when the routing cache is valid, packets can often go directly through the fast path without needing to redo NAT, verification, or routing decisions. But as long as you make changes in the mangle or raw tables, or write rules that break the fast path, the kernel will be forced to let packets enter the slow path. The slow path means: more checks, more calculations, and more state synchronization. Consequently, performance naturally drops. Some mangle rules written casually by some people, or dozens of strange TEE/mark rules, will invisibly drag a large number of packets into the slow path, resulting in a sluggish server even when the CPU is not even fully utilized.

Why do iptables rules in a Docker environment look messier and perform worse? The reason is actually very simple: Docker automatically creates a large number of DNAT mappings, automatically opens FORWARD rules, automatically enables MASQUERADE for SNAT, and inserts various forwarding logic in POSTROUTING. It might be fine with dozens of containers, but with hundreds of containers, these NAT and FORWARD chains become incredibly long. Coupled with the explosion of conntrack entries, an increase in latency is normal. It's not that “your configuration is not optimized enough,” but rather that Docker's default design dictates it cannot be as lightweight as a traditional router.

Of course, the performance of iptables can be optimized. The most common way is to place frequently matched rules at the front, combine a bunch of ports into multiport, combine a large number of IPs into ipset, try to complete filtering logic in INPUT rather than PREROUTING, minimize unnecessary NAT operations, and avoid writing messy mangle tables in PREROUTING, which is highly sensitive to performance. Especially ipset, which allows you to turn what would have been 500 rules into just one. This kind of optimization technique often helps you avoid many performance pitfalls.

To summarize this chapter, one sentence is actually enough: the performance bottleneck of iptables is not “how many rules there are” at all, but “how much extra work the rules force the kernel to do.” Behind every rule you write, the kernel is running a piece of logic. Some logic is as light as a feather, while some is as heavy as a mountain. The difference in how you write them determines whether your server can survive under high traffic.

8 Common iptables commands: these are the only ones you will actually use before writing rules

In the previous chapters, we have already “touched upon” iptables rules in the context of configuring the INPUT chain for a real VPS scenario. I deliberately placed the commands in a specific context rather than explaining the parameters in isolation, in order to help you establish a correct path of judgment first.

But when you actually start maintaining a server in the long run, you will discover another practical problem:You are not “designing rules” every day; most of the time, you are “viewing, adjusting, rolling back, and verifying” existing rules.At this point, what you need is not a complete set of ideas, but a proficient mastery of a few of the most commonly used commands—how to check the current rule status, how to safely insert or delete a rule, and how to confirm whether a certain packet has actually been matched.

Therefore, this chapter no longer discusses “how rules should be designed,” but instead singles out the most commonly used and error-prone iptables operations in actual operations and maintenance, serving as a command toolbox that you can refer to at any time. You can think of it as an “operational supplement” to all the previous chapters, rather than a new main line of learning.


However, the first principle in all operations is always:Observe first, then modify—You must first see clearly what the current rules look like, what the order is, whether Docker has stuffed anything into PREROUTING, and whether NAT is working as you expect... all of this must start with “looking,” for example:

When you suspect that Docker is quietly doing DNAT for you, you only need to check the PREROUTING chain of the nat table. iptables will list the rules chain by chain, in the exact order the kernel executes them, allowing you to quickly determine what is actually happening instead of guessing by intuition.

When you want to see if a certain port has been matched, you can add a log rule at the end of INPUT or FORWARD. This allows you to observe the path without overriding the priority of the key rules ahead of it.

When you want to delete rules, the safest way is to delete them by line number. Because every time you add or delete a rule, the overall order in the chain changes—operating by line number can prevent accidental deletion or deleting the wrong position.

When troubleshooting, always refer back to that “kernel roadmap”. The entry point is always PREROUTING, and the exit is POSTROUTING; local access goes to INPUT; containers, bypasses, and forwarding all go to FORWARD.

You need to look at the commands in combination with the “path”, rather than just staring at the command itself. This way, you will locate problems much faster than those who only memorize commands.


The following commands are the ones I actually use in my daily work, and they are also the actions you won't be able to live without when troubleshooting in the future:Viewing rules, adding rules, deleting rules, and checking if rules are hit. As for those other commands you've never used, there's actually no need to stuff them into your brain.

View the rules of a specific table or chain

iptables -t nat -L PREROUTING -n -v

What it does: Lists the nat table Medium the PREROUTING chain all rules of the PREROUTING chain, displayed in their actual execution order, along with the hit count for each rule. Note that PREROUTING is very often injected with rules by system components (such as Docker, 1Panel, CNI plugins, etc.), so you will often see some jump rules that you didn't write.

When to use: When you want to confirm whether a program is inserting rules into the nat table (especially Docker), or want to check if DNAT, transparent proxy, or port forwarding is working as expected, this command is the first choice.

The output of running this command on my Chicago VPS is as follows:

image.png

View all tables and all chains (complete overview)

iptables -L -n -v

What it does: List all rules in all chains of the filter table, allowing you to see the entire filtering logic at once.

When to use: When you are completely clueless about “who on earth is blocking the packets,” this command is the fastest way to find out.

The output (partial) of running this command on my Chicago VPS is as follows:

image.png

Append a rule to the end of a chain

iptables -A INPUT -s 1.2.3.4 -j DROP

What it does: Add a rule to the end of the INPUT chain without overriding priority.

When to use: When temporarily blocking an IP, debugging paths, or verifying a certain condition, this “append to end” method is the safest.

Insert a rule at the very beginning of a chain

iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT

What it does: Insert a rule at the very beginning of the INPUT chain, making it the highest priority rule.

When to use: When you are preparing to test logic that is easily overridden by preceding rules, or when you are afraid of “accidentally locking yourself out,” use insertion to ensure the rule is executed first.

View the line number of a specific rule in a chain and delete the rule corresponding to that line number (using the Docker chain as an example)

iptables -t nat -L DOCKER --line-numbers -n # 显示nat表中DOCKER链的所有规则及行号
iptables -D DOCKER 5 # 删除行号为3的规则

What it does: Delete rule number 3 in the Docker chain.

When to use: This method is always recommended when deleting rules because the order of rules can change; “deleting by line number” is the least likely to result in accidental deletion.

For example, the output of running this command on my Chicago VPS to view the rule line numbers in the DOCKER chain of the nat table is as follows:

image.png

At this point, runningiptables -D DOCKER 5will directly delete the first DNAT rule at line 5.

View the “hit count” of INPUT rules (very critical)

iptables -L INPUT -n -v

What it does: Display traffic statistics for each rule in the INPUT chain.

When to use: This is the most valuable information when troubleshooting

The output of running this command on my Chicago VPS is as follows:

image.png

Flush a specific chain (using INPUT as an example)

iptables -F INPUT

What it does: Directly flush all rules in the INPUT chain.

When to use: Debugging environments, initializing before writing scripts, and preventing historical rules from interfering with current behavior. Mindless use in production environments is not recommended.

Flush the entire iptables (use with caution!)

iptables -F
iptables -t nat -F

What it does: Clear all rules in the filter and nat tables.

When to use: Can be used when rebuilding the rule system or debugging isolated environments. Try to avoid doing this on actual production servers.

Save current rules (prevent loss after reboot)

Debian/Ubuntu:

iptables-save > /etc/iptables/rules.v4

CentOS/RHEL (using the service tool):

service iptables save

What it does: Persist the rules currently in memory to disk.

When to use: When you have tuned a set of rules and are ready to use them long-term.

If theiptables-persistentcomponent is installed, the corresponding command is:

netfilter-persistent save

Restore rules (load from file)

iptables-restore < /etc/iptables/rules.v4

What it does: Load the saved rules into the system all at once.

When to use: After a system reboot, or when deploying multiple machines in batches.

If theiptables-persistentcomponent is installed, the corresponding command is:

netfilter-persistent reload

Final thoughts: There aren't many commands; the key is “knowing how to read them”

You will find that there are not many of these commands, nor are there any mysterious tricks. What truly gets people stuck is never the commands, butnot knowing which segment of the path the command corresponds to。

Just always remember: the entry point always starts with PREROUTING; local host activity goes through INPUT; forwarding and Docker go through FORWARD; and the exit uniformly goes through POSTROUTING. Then, combined with these “good enough” commands above, you can figure out a direction for most tricky problems within a few minutes.


9 Afterword: written for my future self (and perhaps you, who still don't understand iptables yet)

After writing this entire set of content, I am actually more certain of one thing: an established tool like iptables, though “old,” is by no means obsolete. Its simple logic, the cause-and-effect relationships of rule chains, and the clarity of matching and jumping actually align very well with an engineer's way of thinking. But precisely because it is so flexible and highly tweakable, many people easily get lost when they first encounter it—unable to understand the rules, confused by the relationships between chains, and capable of breaking the network entirely with just one wrong modification.

My purpose in writing this article is not to pile up all the syntax, but toexplain clearly those parts that are most commonly used, most error-prone, and most worth understandingAfter all, most of the time, we don't need to become a “walking encyclopedia of iptables,” but rather to be able to solve problems steadily in real-world scenarios: to judge the intent of a rule when we see it, to roughly infer which chains traffic will pass through based on its direction, and to know where we can make changes and where we must absolutely not touch.

And precisely because of this, I broke down and wrote about some common commands, troubleshooting ideas, and the actual functions of the chains. It's not to make a tutorial, but to give my future self a place to look back and verify the logic when encountering strange NAT phenomena, misrouted Docker traffic, policy routing not taking effect, or port forwarding mysteriously failing.


Actually, for someone like me who is used to configuring standalone firewalls, configuring iptables is where it's easiest to make habitual mistakes. In the world of standalone firewalls, rules are laid out flat across layers of a GUI or policy tree—you change one, and that's it. But on Linux, iptables is not a “firewall configuration file,” but a tool for “editing kernel traffic processing in real time.”

At first, you will subconsciously treat it like a model of “wherever you add a rule, it takes effect,” similar to Palo Alto, FortiGate, or Hillstone. But the reality is quite the opposite—iptables rules are scattered across different tables and chains of different behaviors, and system components will insert things into these chains uninvited. If you don't check the tables, confirm the chains, or observe the actual hit counts, you can easily fall into the illusion of “I clearly added the rule, so why didn't it take effect?”

Ultimately, this is not a technical problem, but a problem of experience migration. Those who are used to standalone firewalls often need to first put aside their past mindset of “firewall as a configuration file” when facing iptables for the first time, and re-examine all rules from the perspective of “kernel real-time traffic processing.” Once this mindset is adjusted, iptables will suddenly “become understandable”—it is no longer a screen full of commands, but a flow control system that you can disassemble line by line and verify step by step.


The iptables tool has a very special quality: once you truly understand it, you feel that the networking world suddenly becomes “explainable.” Things you couldn't understand before can now be deduced all the way along the path; what you used to rely on blind guessing for can now be reasoned out through rules; what you used to suspect was metaphysics can now be solved by capturing packets, checking chains, and looking at counters to find out step-by-step who caused the problem.

Writing up to this point, I have organized the pitfalls I've stepped into over the past few years, the patterns I've figured out, and my systematic way of understanding it. If I encounter more typical problems in the future, I might continue to add to it, but there's no rush, and no need to dictate how much I must write. iptables itself is a tool where you “learn as you use it”—the more you know, the more at ease you will be; keeping a little distance won't diminish its value either.

And so, I'll leave a summary, and leave a light on for my future self.

📌 Content Structure Prompt:
This content belongs to the "Blog Knowledge Map" part, you can view the complete content path from here: Blog Knowledge Map 。
View Related Categories · 3 Matches
📎 Related Articles
Share this article
The blog content is original, please indicate the source when reposting! The RSS address of the blog is:https://blog.tangwudi.com/feed, welcome to subscribe; if needed, you can join theTelegram Groupto discuss questions together.

Comments

  1. handy
    Windows Firefox 150.0
    5 months ago
    2026-5-11 20:09:15

    I also read it carefully. What I learned before was just half-baked. Thanks to the expert for the explanation.

    • Tang Wudi
      handy
      Macintosh Chrome 147.0.0.0
      5 months ago
      2026-5-11 20:14:07

      You're welcome, I'm glad it's useful. I also felt that my previous understanding wasn't deep enough, which is why I specifically wrote an article to push myself to think it through.

  2. Windows Firefox 147.0
    9 months ago
    2026-1-24 12:44:22

    Thank you, expert, for the detailed explanation of iptables. I really read it word by word, and then I gained a new understanding of iptables. Things that I couldn't figure out before have become much clearer. Thank you.

    • Tang Wudi
      DNS123
      Macintosh Chrome 144.0.0.0
      9 months ago
      2026-1-24 14:04:11

      I also habitually organize things because I tend to forget easily, so I try to write as detailed as possible. However, I didn't expect you to actually read it word by word. These days, most people don't have the patience to finish reading long articles.

Send Comment Edit Comment


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗ
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
Kaomoji
Emoji
Little Dinosaur
Flower!
Previous
Next
       

👋 Welcome to “Wudi's Personal Blog”

Here, long-term exploration is mainly carried out around the following directions:

🧱 Personal Digital Infrastructure and Blog System Construction
☁️ Cloudflare and Network Architecture Practice
🧠 AI and Knowledge System Exploration
🛡️ Network Security and Access Optimization
🎵 Music and Sound Cognition
👁️ Cognitive Perspectives and Worldviews