← Назад к вопросам

Для чего нужен префикс x- в заголовках HTTP?

2.0 Middle🔥 101 комментариев
#REST API и микросервисы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Префикс X- в HTTP заголовках

Префикс X- в HTTP заголовках исторически использовался для обозначения экспериментальных, нестандартных или проприетарных заголовков, которые не входят в официальный стандарт HTTP.

История и контекст

1. Исходное назначение

В ранние дни веб-разработки разработчики добавляли свои заголовки для передачи дополнительной информации между клиентом и сервером. Чтобы избежать конфликтов с будущими стандартными заголовками, они префиксировали их X-:

X-Custom-Header: value
X-User-Agent: MyApp/1.0
X-Request-ID: abc123

2. RFC 6648 и изменение подхода

В 2012 году RFC 6648 рекомендовал отказаться от префикса X- для новых заголовков. Однако многие существующие заголовки с X- продолжают использоваться.

Распространённые X- заголовки

1. X-Requested-With

Обычно используется фреймворками для определения AJAX запросов:

@RestController
@RequestMapping("/api")
public class UserController {
    
    @GetMapping("/users")
    public List<User> getUsers(
            @RequestHeader(value = "X-Requested-With", required = false) String requestedWith) {
        
        if ("XMLHttpRequest".equals(requestedWith)) {
            // AJAX запрос
            System.out.println("AJAX request detected");
        }
        
        return userService.getAllUsers();
    }
}

2. X-Request-ID

Уникальный идентификатор для отслеживания запроса через логи:

@Component
public class RequestIdFilter implements Filter {
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String requestId = httpRequest.getHeader("X-Request-ID");
        
        if (requestId == null || requestId.isEmpty()) {
            requestId = UUID.randomUUID().toString();
        }
        
        MDC.put("requestId", requestId);
        
        try {
            chain.doFilter(request, response);
        } finally {
            MDC.remove("requestId");
        }
    }
}

// Логирование будет содержать requestId:
// 2024-01-15 10:30:45 [abc123-def456] INFO: User authenticated

3. X-API-Key

Передача API ключа для авторизации:

@Component
@Aspect
public class ApiKeyValidator {
    
    @Before("@annotation(ValidateApiKey)")
    public void validateApiKey(JoinPoint joinPoint) {
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = ((ServletRequestAttributes) attributes).getRequest();
        
        String apiKey = request.getHeader("X-API-Key");
        
        if (apiKey == null || apiKey.isEmpty()) {
            throw new UnauthorizedException("X-API-Key header is missing");
        }
        
        if (!isValidApiKey(apiKey)) {
            throw new UnauthorizedException("Invalid API key");
        }
    }
    
    private boolean isValidApiKey(String apiKey) {
        // Проверка в БД или конфиге
        return apiKeyService.isValid(apiKey);
    }
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ValidateApiKey {
}

@RestController
public class DataController {
    
    @GetMapping("/data")
    @ValidateApiKey
    public ResponseEntity<Data> getData() {
        return ResponseEntity.ok(dataService.getData());
    }
}

4. X-Forwarded-For

Передача оригинального IP адреса клиента через прокси:

@Component
public class ClientIpFilter implements Filter {
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String clientIp = getClientIpAddress(httpRequest);
        
        System.out.println("Client IP: " + clientIp);
        
        chain.doFilter(request, response);
    }
    
    private String getClientIpAddress(HttpServletRequest request) {
        String xForwardedFor = request.getHeader("X-Forwarded-For");
        
        if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
            // X-Forwarded-For может содержать несколько IP: client, proxy1, proxy2
            return xForwardedFor.split(",")[0].trim();
        }
        
        String xRealIp = request.getHeader("X-Real-IP");
        if (xRealIp != null && !xRealIp.isEmpty()) {
            return xRealIp;
        }
        
        return request.getRemoteAddr();
    }
}

5. X-Frame-Options

Защита от Clickjacking атак:

@Configuration
public class SecurityConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.headers()
            .frameOptions()
            .sameOrigin() // Эквивалент X-Frame-Options: SAMEORIGIN
            .and()
            .and()
            .csrf()
            .and()
            .authorizeRequests()
            .anyRequest()
            .authenticated();
        
        return http.build();
    }
}

6. X-CSRF-Token

Токен для защиты от CSRF атак:

@RestController
@RequestMapping("/api")
public class FormController {
    
    @PostMapping("/submit")
    public ResponseEntity<String> submitForm(
            @RequestHeader("X-CSRF-Token") String csrfToken,
            @RequestBody FormData data) {
        
        if (!validateCsrfToken(csrfToken)) {
            return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
        }
        
        // Обработка формы
        return ResponseEntity.ok("Form submitted");
    }
}

7. X-Content-Type-Options

Предотвращение MIME-type sniffing:

@Configuration
public class SecurityConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.headers()
            .contentSecurityPolicy("default-src 'self'")
            .and()
            .xssProtection()
            .and()
            .contentTypeOptions(); // X-Content-Type-Options: nosniff
        
        return http.build();
    }
}

Практический пример: Кастомная авторизация

@RestController
@RequestMapping("/api/v1")
public class SecureController {
    
    @GetMapping("/profile")
    public ResponseEntity<UserProfile> getProfile(
            @RequestHeader("X-User-ID") String userId,
            @RequestHeader("X-Session-Token") String sessionToken,
            @RequestHeader(value = "X-Request-ID", required = false) String requestId) {
        
        if (requestId == null) {
            requestId = UUID.randomUUID().toString();
        }
        
        MDC.put("requestId", requestId);
        
        try {
            // Валидация токена
            if (!sessionService.isValidToken(userId, sessionToken)) {
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
            }
            
            UserProfile profile = userService.getProfile(userId);
            return ResponseEntity.ok(profile);
        } finally {
            MDC.remove("requestId");
        }
    }
}

Когда использовать X- заголовки

  • Для проприетарной информации (API ключи, custom data)
  • Для отслеживания запросов (Request ID)
  • Для AJAX детектирования
  • Для безопасности (CSRF токены, IP адреса)
  • Для специфичного для вашего приложения функционала

Резюме

Префикс X- используется для:

  • Обозначения нестандартных заголовков
  • Передачи приватной или проприетарной информации
  • Отслеживания и аудита запросов
  • Реализации дополнительной безопасности
  • Интеграции с прокси и балансировщиками нагрузки
Для чего нужен префикс x- в заголовках HTTP? | PrepBro