The CoreDNS rewrite plugin is less about changing what DNS records mean and more about changing how DNS queries and responses are expressed to fit different network or operational needs.

Let’s see it in action. Imagine you have a service that’s internally registered as api.internal.corp but external clients need to access it via api.external.com. We can use rewrite to handle this translation seamlessly.

Here’s a snippet from a CoreDNS Corefile:

.:53 {
    rewrite name api.internal.corp api.external.com
    forward . 8.8.8.8
}

When a client queries for api.internal.corp, CoreDNS, hitting this rule, will internally rewrite the query to api.external.com before it forwards the request to 8.8.8.8. The response from 8.8.8.8 (which will likely be for api.external.com) will then be returned to the client, but CoreDNS will rewrite it back to api.internal.corp before sending it. The client never sees api.external.com.

This solves a few common problems. Firstly, it allows you to manage internal service discovery names independently of external-facing DNS. You can rename internal services, update their DNS, and use rewrite to keep the external view consistent without affecting internal clients. Secondly, it’s invaluable for migrating services. You can gradually shift clients to a new domain name by rewriting the old one to the new one, eventually removing the rewrite rule once all clients have transitioned.

Internally, the rewrite plugin works by inspecting the Name field of a DNS request (dns.Msg.Question[0].Name). If a rule matches, it modifies this Name field in place before the request proceeds to the next plugin in the chain (like forward or cache). For responses, it performs a similar transformation, looking at the Name field of the RDATA in the answer section and rewriting it. The matching process is a simple string equality check by default.

The rewrite plugin supports several directives. The most common is rewrite name <from> <to>. This directive rewrites the queried name from <from> to <to>. You can also use rewrite stop to prevent further rewrite rules from being applied if a match is found, and rewrite continue to keep processing subsequent rules.

Consider a more complex scenario where you need to rewrite a subdomain. If you have service.dev.corp and want it to resolve to service.prod.corp, you’d use:

.:53 {
    rewrite name service.dev.corp service.prod.corp
    forward . 8.8.8.8
}

This is straightforward. However, the real power comes with wildcards and regular expressions. For instance, to rewrite all subdomains of *.internal.corp to *.external.com, you might think of a simple wildcard, but rewrite uses glob-style wildcards. For more sophisticated pattern matching and replacement, you’d typically use the regex plugin, but rewrite does have basic wildcard support for exact name matches.

A key aspect is that rewrite operates on the name itself, not the IP address or other record data. This means it’s perfect for manipulating the domain names in A, AAAA, CNAME, and other records where the name is the primary identifier. If you need to manipulate IP addresses, you’d look at other plugins or combine rewrite with something like iprange or custom Lua scripting.

The rewrite plugin is particularly useful when dealing with legacy systems that might have hardcoded internal DNS names but need to be exposed externally under a different, more standard naming convention. It acts as a transparent translation layer, meaning the client making the DNS query doesn’t need to be aware that any translation is happening. The rewrite plugin is applied before the query is sent to upstream resolvers and before the response is sent back to the client, allowing for bidirectional name translation. This is crucial for maintaining consistency across different operational environments.

The plugin’s behavior is deterministic: if a name matches a rule, it’s rewritten. If it doesn’t match any rule, it passes through unchanged. You can chain multiple rewrite rules, and the order matters. If you have rewrite name a b and rewrite name b c, a query for a will first be rewritten to b, and then that b will be rewritten to c. This allows for sequential transformations.

When rewriting responses, the plugin examines the RDATA of the records in the ANSWER section. If the RDATA contains a domain name that matches a configured rewrite rule, that name is transformed. This is essential for ensuring that clients receive a consistent view of the network, even if the underlying DNS infrastructure uses different naming schemes. For example, if an internal server returns a CNAME record pointing to internal-service.local, and you want external clients to see external-service.global, the rewrite plugin can transform that CNAME response before it reaches the client.

The next challenge you’ll likely encounter is when you need more complex pattern matching than simple string replacement, leading you to the regex plugin.

Want structured learning?

Take the full Coredns course →