MicroservicesSecuritybeginner
Updated:

Microservices Security Best Practices

6 min read

Secure a distributed system the right way. Learn API gateway authentication, JWT validation, mTLS, secrets management and least privilege with practical examples.

TL;DR – Quick Answer

Secure microservices by authenticating requests at the API gateway, passing a signed JWT to downstream services that each validate it, and encrypting service-to-service traffic with mutual TLS. Add least-privilege access, external secrets management and input validation on every service. The guiding idea is zero trust: never assume a request is safe just because it came from inside the network.

On This Page

Splitting a monolith into microservices multiplies your attack surface. What used to be one process behind one login is now a dozen services talking over the network, each an entry point an attacker could target. Security stops being a wall around the building and becomes a lock on every door inside it. The organising principle for that shift has a name: zero trust, meaning no request is trusted simply because it came from inside.

This tutorial walks through the practices that matter most, from authenticating at the edge to encrypting internal traffic. It assumes you know how services communicate in a microservices architecture; here we harden those communication paths.

Authenticate once at the edge, verify everywhere

The first decision is where login happens. Duplicating username-and-password logic in every service is a maintenance and security nightmare. Instead, authenticate the user once at the API gateway, then issue a signed token that downstream services validate.

The gateway becomes your single front door. It checks credentials, applies rate limiting, and rejects malformed requests before they ever reach an internal service. But — and this is the part beginners miss — downstream services must still validate the token themselves. Trusting the gateway blindly means one bypassed gateway compromises everything.

Concern Handle at gateway Handle at each service
Login / credential check Yes No
Rate limiting, IP filtering Yes Rarely
Token (JWT) validation Yes Yes, again
Fine-grained authorisation (roles) Sometimes Yes
Input validation Basic Always

Stateless auth with JWT

A JWT (JSON Web Token) is a signed token carrying the user's identity and claims such as roles. Because it is signed, any service can verify it with the signing key — no shared session database, no lookup, no coordination. That statelessness is exactly why JWTs fit independently scaled services so well.

Here is a Spring Security configuration that validates incoming JWTs on a resource service. Every request must present a valid token, and Spring rejects anything unsigned or expired automatically.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/actuator/health").permitAll()
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated())
            .oauth2ResourceServer(oauth -> oauth.jwt(org.springframework.security.config
                .Customizer.withDefaults()))
            .csrf(csrf -> csrf.disable());   // stateless API, no browser session
        return http.build();
    }
}

With oauth2ResourceServer().jwt(), Spring validates the token's signature and expiry against a configured public key or JWKS endpoint. The hasRole("ADMIN") line shows authorisation: even a valid token cannot reach /admin/** unless it carries the ADMIN role. Authentication proves who you are; authorisation decides what you may do.

Common mistake: Storing sensitive data or long-lived sessions inside the JWT payload. A JWT is signed, not encrypted — anyone can decode and read its contents. Never put passwords or secrets in claims, and keep expiry short (minutes, not days) with a refresh-token flow for renewal.

Encrypt internal traffic with mutual TLS

Perimeter security is not enough. If an attacker gets a foothold inside your network, unencrypted service-to-service calls let them eavesdrop and impersonate services. Mutual TLS (mTLS) fixes this: both the calling service and the receiving service present certificates and verify each other. Only services holding a trusted certificate can talk.

Plain TLS proves the server's identity to the client, the way your browser trusts a website. mTLS adds the reverse: the server also demands proof of the client's identity. In a zero-trust cluster, that means service A cannot call service B just by knowing its address — it must present a valid certificate B recognises.

In practice a service mesh like Istio or Linkerd handles mTLS transparently, so your application code stays clean while every hop between pods is encrypted and mutually authenticated. You should be able to explain the concept even before you operate a mesh.

Least privilege and secrets

Two habits prevent a large share of real breaches, and neither requires fancy tooling.

Least privilege means every service, database account and token gets the minimum access it needs and nothing more. The order service should have read-write access to the orders table and no access at all to the payments database. If that service is compromised, the blast radius is limited to what it could already touch.

Secrets management means passwords, API keys and certificates never live in source code or container images. Hardcode a database password and it ends up in Git history, in the built image, and probably in a log line somewhere — and rotating it means rebuilding and redeploying. Instead, pull secrets from a dedicated store at runtime.

# Kubernetes injects the secret as an environment variable at runtime;
# the value never appears in the image or the source repository.
env:
  - name: SPRING_DATASOURCE_PASSWORD
    valueFrom:
      secretKeyRef:
        name: orders-db-credentials
        key: password

The application reads SPRING_DATASOURCE_PASSWORD like any environment variable, but the value is supplied by Kubernetes Secrets (or Vault, or AWS Secrets Manager) at container start. Rotating the password becomes a config change, not a code change.

Pro tip: Add secret scanning to your CI pipeline. Tools that reject a commit containing something that looks like an API key catch the most common mistake — a developer pasting a real credential into a config file "just to test" and forgetting to remove it. Prevention at commit time beats cleanup after a leak.

Validate every input and fail safely

Each service exposes an API, and every API is an attack surface. Validate all incoming data at the boundary: reject payloads that are too large, fields that are the wrong type, and strings that exceed sane lengths. This blunts injection attacks and malformed-request denial of service.

Failing safely matters too. When a downstream dependency misbehaves, your service should degrade gracefully rather than leak stack traces or hang. This is where security and resilience meet — a circuit breaker not only improves reliability but also stops an attacker from tying up all your threads by hammering a slow endpoint.

A layered checklist

Security in microservices is never a single control; it is layers that each assume the previous one might fail. Put together, a solid baseline looks like this:

  • Authenticate users once at the gateway, and validate the JWT again in every service.
  • Authorise by role or scope at the service that owns the data, not only at the edge.
  • Encrypt internal traffic with mTLS so a network foothold does not become full access.
  • Grant least privilege to every service account, database user and token.
  • Keep secrets in a manager and inject them at runtime; never in code or images.
  • Validate all input at each boundary and fail safely under load.

Adopt these as defaults and you have covered the failure modes that account for the majority of real-world incidents.

Why interviewers probe security

Most freshers can build a working API; far fewer can talk about protecting it. When an interviewer asks "how would you secure communication between two microservices?", they are checking whether you think past the happy path. A strong answer covers edge authentication, per-service JWT validation, and encrypting internal calls with mTLS — and mentions that internal traffic still needs protection because of zero trust.

Bring up least privilege and externalised secrets and you sound like someone who has read a real post-mortem. You do not need to have configured a service mesh; you need to reason about the layers clearly. Rehearse these points with the microservices interview questions guide, and build the muscle memory on real services in the Java Full Stack with AI program, where you secure a Spring Boot API end to end. Start from Spring Boot and Spring Security basics if authentication is still new, then come back and layer on the network-level controls covered here.

Frequently Asked Questions

Where should authentication happen in microservices?
Authenticate the user once at the edge, typically the API gateway, then pass a signed token to downstream services. Each service still validates that token rather than blindly trusting it. This keeps login logic in one place while ensuring every service independently verifies who the caller is.
What is a JWT and why is it common in microservices?
A JWT, or JSON Web Token, is a signed token that carries user identity and claims like roles. It is stateless, so any service can verify it using the signing key without a database lookup or shared session store. That statelessness is what makes it fit naturally with independently scaled microservices.
What is mutual TLS and why use it between services?
Mutual TLS, or mTLS, means both the client service and the server service present certificates and verify each other, not just the client verifying the server. It encrypts internal traffic and ensures only trusted services can call each other. It is a core part of zero-trust networking inside a cluster.
What does zero trust mean for microservices?
Zero trust means you never assume a request is safe just because it originated inside your network. Every service authenticates and authorises every request, internal or external. This limits the damage if an attacker breaches the perimeter, because they still cannot freely call internal services.
Why should secrets never be hardcoded in microservices?
Hardcoded passwords and API keys end up in source control, container images and logs, where anyone with access can read them. Instead, store secrets in a dedicated manager like Vault, AWS Secrets Manager or Kubernetes Secrets and inject them at runtime. Rotating a leaked hardcoded secret means rebuilding and redeploying, which is slow and error-prone.
Do freshers need to know microservices security for interviews?
You will not be asked to design a bank's security architecture, but you should explain edge authentication, JWT validation, and why internal calls still need protection. Mentioning least privilege and secrets management shows maturity. It signals you think about security as a design concern, not an afterthought.

Want to Build Your Career in Java Full Stack with AI?

Join CodeBegun and train with working industry engineers — Explore the Program

Apply for Demo Class →
Siva Prasad Galaba
Founder, CodeBegun · Staff Engineer

Founder of CodeBegun. 15+ years building Java systems at companies like Crunchyroll. Teaches Java, Spring Boot and system design the way the industry actually works, and mentors students through projects, mock interviews and placement preparation.

Technically reviewed by CodeBegun Technical TeamLast reviewed 15 July 2026 LinkedIn
Chat with us