Backend Development 8 min read

Implementing Image Anti-Hotlinking in Spring Boot with a Configurable Interceptor

This article demonstrates how to implement image anti-hotlinking in a Spring Boot application by creating a configurable interceptor, registering it, and providing flexible YAML settings, while discussing limitations such as referer spoofing and potential false positives.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Implementing Image Anti-Hotlinking in Spring Boot with a Configurable Interceptor

Introduction

For security reasons we may want backend‑served images to be displayed only on a specific website, preventing crawlers from downloading them, and for performance we want to avoid other sites hotlinking our images and consuming server resources.

1. Simple Implementation

1‑1 Create Interceptor Class

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ImageProtectionInterceptor implements HandlerInterceptor {
    private static final String ALLOWED_DOMAIN = "baidudu.com"; // allowed domain

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // Get request URL
        String requestUrl = request.getRequestURL().toString();

        // Check if request ends with image suffix
        if (requestUrl.endsWith(".jpg") || requestUrl.endsWith(".png") || requestUrl.endsWith(".jpeg")) {
            // Get Referer header
            String referer = request.getHeader("Referer");

            // Allow if referer contains allowed domain
            if (referer != null && referer.contains(ALLOWED_DOMAIN)) {
                return true; // allow
            } else {
                response.sendError(HttpServletResponse.SC_FORBIDDEN);
                return false; // block
            }
        }
        return true; // allow non‑image resources
    }
}

1‑2 Register Interceptor

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // Register interceptor for all paths
        registry.addInterceptor(new ImageProtectionInterceptor())
                .addPathPatterns("/**"); // intercept all requests
    }
}

2. Flexible Configuration

2‑1 application.yml

# Image anti‑hotlinking configuration
img-protect:
  enabled: true
  allowBrowser: false
  allowReferer: baidudu.com

2‑2 Configuration Properties Class

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties("img-protect")
public class ImgProtectConfig {
    private boolean enabled;
    private boolean allowBrowser;
    private String allowReferer;

    public boolean getEnabled() { return enabled; }
    public void setEnabled(boolean enabled) { this.enabled = enabled; }
    public boolean getAllowBrowser() { return allowBrowser; }
    public void setAllowBrowser(boolean allowBrowser) { this.allowBrowser = allowBrowser; }
    public String getAllowReferer() { return allowReferer; }
    public void setAllowReferer(String allowReferer) { this.allowReferer = allowReferer; }
}

2‑3 Flexible Interceptor

import 上方2-2创建的类路径.ImgProtectConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

@Component
public class ImageProtectionInterceptor implements HandlerInterceptor {
    @Autowired
    private ImgProtectConfig imgProtectConfig;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // Check if feature is enabled
        if (!imgProtectConfig.getEnabled()) {
            return true;
        }
        String requestUrl = request.getRequestURL().toString();
        if (requestUrl.endsWith(".jpg") || requestUrl.endsWith(".png") || requestUrl.endsWith(".jpeg")) {
            String referer = request.getHeader("Referer");
            if (referer == null && imgProtectConfig.getAllowBrowser()) {
                return true; // allow direct browser access
            } else if (referer != null && isAllowedDomain(referer)) {
                return true; // allowed referer
            } else {
                response.sendError(HttpServletResponse.SC_FORBIDDEN);
                return false;
            }
        }
        return true;
    }

    // Check if referer is in whitelist
    private boolean isAllowedDomain(String referer) {
        String allowedReferers = imgProtectConfig.getAllowReferer();
        if (allowedReferers != null && !"".equals(allowedReferers.trim())) {
            Set
allowedDomains = new HashSet<>(Arrays.asList(allowedReferers.split(",")));
            for (String allowedDomain : allowedDomains) {
                if (referer.contains(allowedDomain.trim())) {
                    return true;
                }
            }
        }
        return false;
    }
}

2‑4 Register Flexible Interceptor

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private ImageProtectionInterceptor imageProtectionInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // Register interceptor for all paths
        registry.addInterceptor(imageProtectionInterceptor)
                .addPathPatterns("/**"); // intercept all requests
    }
}

Conclusion

The above anti‑hotlinking interceptors can handle typical image hotlinking scenarios, but they cannot guarantee absolute security; issues such as referer spoofing, false positives/negatives, and proxy tricks may still arise.

Javabackend developmentSpring BootInterceptoranti-hotlinkingImage Protection
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.