You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何连接Angular+Nginx Docker容器与Spring后端并解决CORS错误

CORS错误与Nginx 405错误排查与解决

问题描述

每次前端向服务器发起请求都遇到CORS错误。前端容器通过curl请求同网络的后端容器正常,本地运行容器也无问题。Postman测试时Nginx返回405错误,Spring后端未收到请求;Firefox中请求无法发送,仅返回CORS错误。

现有配置

Nginx default.conf配置

server {
  listen 80;
  sendfile on;
  default_type application/octet-stream;

  gzip on;
  gzip_http_version 1.1;
  gzip_disable      "MSIE [1-6]\.";
  gzip_min_length   256;
  gzip_vary         on;
  gzip_proxied      expired no-cache no-store private auth;
  gzip_types        text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
  gzip_comp_level   9;

  root /usr/share/nginx/html;

  location / {
    try_files $uri $uri/ /index.html =404;
  }
}

Docker Compose配置

version: "3.5"
services:
  backend:
    image: spring_backend
    container_name: spring
    build:
      context: ./
    expose:
      - 8080
    restart: always
    networks:
      - reverse-proxy

  frontend:
    image: angular_frontend
    container_name: angular
    build: https://github.com/some_repo/frontend.git
    ports:
      - "80:80"
    restart: always
    networks:
      - reverse-proxy

networks:
  reverse-proxy:
    name: reverse-proxy

Angular环境配置

export const environment = {
  url: 'http://spring:8080/',
  production: true,
};

Spring后端Security配置

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final PersonDetailsService personDetailsService;
    private final JWTFilter jwtFilter;

    @Autowired
    public SecurityConfig(PersonDetailsService personDetailsService, JWTFilter jwtFilter){
        this.personDetailsService = personDetailsService;
        this.jwtFilter = jwtFilter;
    }

    @SneakyThrows(Exception.class)
    protected void configure(HttpSecurity http) {
        http
                .csrf().disable()
                .cors().configurationSource(corsConfigurationSource())
                .and()
                .authorizeRequests().antMatchers("/login","/register").permitAll()
                .anyRequest().hasAnyRole("USER")
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
    }

    @SneakyThrows(Exception.class)
    protected void configure(AuthenticationManagerBuilder auth) {
        auth.userDetailsService(personDetailsService);
    }
    @Bean
    public PasswordEncoder getPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    @SneakyThrows(Exception.class)
    public AuthenticationManager authenticationManagerBean() {
        return super.authenticationManagerBean();
    }
    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.addAllowedOrigin("*");
        configuration.addAllowedHeader("*");
        configuration.addAllowedMethod("*");
        configuration.addExposedHeader("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

}

问题分析与解决方案

核心问题

  1. 前端地址配置错误:Angular中使用http://spring:8080/是Docker内部容器域名,浏览器无法解析该地址,实际请求会发送到前端Nginx的80端口,导致Nginx将API请求当作静态资源处理,返回index.html,引发405错误和CORS问题。
  2. Nginx未配置反向代理:当前Nginx仅处理前端静态文件,未将API请求转发到后端容器。

解决步骤

  1. 修改Angular环境配置
    将后端地址改为相对路径,让请求先发送到前端Nginx:

    export const environment = {
      url: '/api/',
      production: true,
    };
    
  2. 更新Nginx default.conf配置
    添加反向代理规则,将/api/开头的请求转发到后端Spring容器:

    server {
      listen 80;
      sendfile on;
      default_type application/octet-stream;
    
      gzip on;
      gzip_http_version 1.1;
      gzip_disable      "MSIE [1-6]\.";
      gzip_min_length   256;
      gzip_vary         on;
      gzip_proxied      expired no-cache no-store private auth;
      gzip_types        text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
      gzip_comp_level   9;
    
      root /usr/share/nginx/html;
    
      # 反向代理后端API请求
      location /api/ {
        proxy_pass http://spring:8080/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
      }
    
      location / {
        try_files $uri $uri/ /index.html =404;
      }
    }
    
  3. 验证后端CORS配置
    后端已配置允许所有来源,通过Nginx反向代理后,请求Origin会是前端域名,后端配置可保持不变;若需更严谨,可将addAllowedOrigin("*")改为实际前端域名(如http://your-domain.com)。

  4. 重建并重启容器
    重新构建Angular镜像(因环境配置修改),再启动所有容器:

    docker-compose down
    docker-compose build frontend
    docker-compose up -d
    

额外说明

  • 前端容器内curl能访问后端,是因为容器处于Docker内部网络可解析spring域名,但浏览器运行在宿主机,无法识别该内部域名,必须通过Nginx转发。
  • 405错误源于Nginx将API请求当作静态文件处理,配置反向代理后即可解决。

内容的提问来源于stack exchange,提问作者magik455

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 11:26:12