ทำไม nginx ตอบสนองต่อชื่อโดเมนใด ๆ


139

ฉันมี nginx และทำงานกับแอพ Ruby / Sinatra และทั้งหมดนั้นก็ดี อย่างไรก็ตามตอนนี้ฉันกำลังพยายามให้แอปพลิเคชั่นที่สองทำงานจากเซิร์ฟเวอร์เดียวกันและฉันสังเกตเห็นบางสิ่งที่แปลก ก่อนอื่นนี่คือ nginx.conf ของฉัน:

pid /tmp/nginx.pid;
error_log /tmp/nginx.error.log;

events {
  worker_connections 1024;
  accept_mutex off;
}

http {
  default_type application/octet-stream;
  access_log /tmp/nginx.access.log combined;

  sendfile on;
  tcp_nopush on;
  tcp_nodelay off;

  gzip on;
  gzip_http_version 1.0;
  gzip_proxied any;
  gzip_min_length 500;
  gzip_disable "MSIE [1-6]\.";
  gzip_types text/plain text/xml text/css
             text/comma-separated-values
             text/javascript application/x-javascript
             application/atom+xml;

  upstream app {
    server unix:/var/www/app/tmp/sockets/unicorn.sock fail_timeout=0;
  }

  server {
    listen 80;
    client_max_body_size 4G;
    server_name FAKE.COM;

    keepalive_timeout 5;

    root /var/www/app/public;

    location / {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;

      if (!-f $request_filename) {
        proxy_pass http://app;
        break;
      }
    }

    error_page 500 502 503 504 /500.html;
    location = /500.html {
      root /var/www/app/public;
    }
  }
}
                                                          68,0-1        B

โปรดสังเกตว่าserver_nameมีการตั้งค่าอย่างไรFAKE.COMเซิร์ฟเวอร์จะตอบสนองต่อโฮสต์ทั้งหมดที่เข้าถึงเซิร์ฟเวอร์นั้นผ่านชื่อโดเมนอื่น ฉันจะทำให้เซิร์ฟเวอร์นั้นตอบสนองต่อการร้องขอได้FAKE.COMอย่างไร


คำสั่งกรองไม่listen fake.com | something.com:80 server_name
Alexei Martchenko

คำตอบ:


202

บล็อกเซิร์ฟเวอร์แรกในการกำหนดค่า nginx เป็นค่าเริ่มต้นสำหรับคำขอทั้งหมดที่เข้าชมเซิร์ฟเวอร์ที่ไม่มีเซิร์ฟเวอร์บล็อกเฉพาะ

ดังนั้นในการกำหนดค่าของคุณสมมติว่าโดเมนที่แท้จริงของคุณคือ REAL.COM เมื่อผู้ใช้พิมพ์ว่าจะแก้ไขเซิร์ฟเวอร์ของคุณและเนื่องจากไม่มีบล็อกเซิร์ฟเวอร์สำหรับการตั้งค่านี้บล็อกเซิร์ฟเวอร์สำหรับ FAKE.COM เป็นคนแรก เซิร์ฟเวอร์บล็อก (บล็อกเซิร์ฟเวอร์เฉพาะในกรณีของคุณ) จะดำเนินการตามคำขอ

นี่คือเหตุผลที่การกำหนดค่า Nginx ที่เหมาะสมมีบล็อกเซิร์ฟเวอร์ที่เฉพาะเจาะจงสำหรับค่าเริ่มต้นก่อนที่จะติดตามกับผู้อื่นสำหรับโดเมนที่ระบุ

# Default server
server {
    return 404;
}

server {
    server_name domain_1;
    [...]
}

server {
    server_name domain_2;
    [...]
}

ฯลฯ

** แก้ไข **

ดูเหมือนว่าผู้ใช้บางคนสับสนเล็กน้อยจากตัวอย่างนี้และคิดว่ามันถูก จำกัด ไว้ที่ไฟล์ conf เดียวและอื่น ๆ

โปรดทราบว่าข้างต้นเป็นตัวอย่างง่าย ๆ สำหรับ OP ในการพัฒนาตามที่ต้องการ

ฉันเองใช้ไฟล์ vhost conf แยกกันด้วยเหตุนี้ (CentOS / RHEL):

http {
    [...]
    # Default server
    server {
        return 404;
    }
    # Other servers
    include /etc/nginx/conf.d/*.conf;
}

/etc/nginx/conf.d/ จะมี domain_1.conf, domain_2.conf ... domain_n.conf ซึ่งจะรวมอยู่หลังจากบล็อกเซิร์ฟเวอร์ในไฟล์หลัก nginx.conf ซึ่งจะเป็นชื่อแรกเสมอและจะเป็นค่าเริ่มต้นเสมอเว้นแต่จะมีการแทนที่ด้วย default_server คำสั่งอื่น ๆ

ลำดับตัวอักษรของชื่อไฟล์ของไฟล์ conf สำหรับเซิร์ฟเวอร์อื่น ๆ จะไม่เกี่ยวข้องในกรณีนี้

นอกจากนี้การจัดเรียงนี้ให้ความยืดหยุ่นมากมายซึ่งเป็นไปได้ที่จะกำหนดค่าเริ่มต้นหลายรายการ

ในกรณีเฉพาะของฉันฉันมี Apache ฟังบนพอร์ต 8080 บนอินเทอร์เฟซภายในเท่านั้นและฉันพร็อกซี PHP และ Perl สคริปต์กับ Apache

อย่างไรก็ตามฉันรันแอพพลิเคชั่นสองตัวแยกกันซึ่งทั้งคู่เชื่อมโยงกลับด้วย ": 8080" ใน html เอาท์พุทที่แนบมาเนื่องจากตรวจพบว่า Apache ไม่ได้ทำงานบนพอร์ตมาตรฐาน 80 และลอง "ช่วย" ฉันออก

สิ่งนี้ทำให้เกิดปัญหาว่าลิงก์ไม่ถูกต้องเนื่องจาก Apache ไม่สามารถติดต่อได้จากอินเทอร์เฟซภายนอกและลิงก์ควรชี้ไปที่พอร์ต 80

ฉันแก้ไขปัญหานี้โดยการสร้างเซิร์ฟเวอร์เริ่มต้นสำหรับพอร์ต 8080 เพื่อเปลี่ยนเส้นทางคำขอดังกล่าว

http {
    [...]
    # Default server block for undefined domains
    server {
        listen 80;
        return 404;
    }
    # Default server block to redirect Port 8080 for all domains
    server {
        listen my.external.ip.addr:8080;
        return 301 http://$host$request_uri;
    }
    # Other servers
    include /etc/nginx/conf.d/*.conf;
}

เนื่องจากไม่มีสิ่งใดในบล็อกเซิร์ฟเวอร์ปกติฟังบนพอร์ต 8080 บล็อกเซิร์ฟเวอร์เริ่มต้นการเปลี่ยนเส้นทางจะจัดการการร้องขอดังกล่าวอย่างโปร่งใสโดยอาศัยตำแหน่งใน nginx.conf

จริง ๆ แล้วฉันมีบล็อกเซิร์ฟเวอร์สี่รายการและนี่เป็นกรณีการใช้งานที่ง่ายขึ้น


1
Nginx ต้องการตัวพิมพ์เล็ก - "เซิร์ฟเวอร์" ควรเป็นเซิร์ฟเวอร์และ "ส่งคืน" ควรส่งคืน หวังว่านี่จะช่วยลดปัญหาเล็กน้อยเมื่อคัดลอกรหัสนี้
Capaj

2
คงที่ดูคำตอบของ Oleg Neumyvkin - หากคุณมีไฟล์กำหนดค่าหลายไฟล์ในเว็บไซต์พร้อมใช้งานเซิร์ฟเวอร์แรกในไฟล์แรกตามลำดับตัวอักษรคือค่าเริ่มต้นของคุณ ฉันสงสัยว่านี่อาจเป็นปัญหา นอกจากนี้การกระจายจำนวนมากเรียกใช้ 'nginx -t' เพื่อทดสอบการกำหนดค่าก่อนที่จะเริ่มระบบใหม่ - คุณอาจมีข้อผิดพลาดในการป้องกันการเริ่มต้นใหม่
jwhitlock

2
สิ่งนี้ไม่สมบูรณ์และไม่ควรเป็นคำตอบที่ยอมรับได้ เพื่อให้สามารถใช้งานได้คุณจะต้องลบ default_server ออกจากคำสั่งการฟังใด ๆ
Ben

@ben ประการแรก OP ไม่ได้มี "default_server" ในตัวอย่างปัญหาของเขาและคำตอบนั้นเหมาะกับคำถามเฉพาะของคำถามนั้น ประการที่สองทำไมทุกคนจะกำหนด default_server ในตำแหน่งเซิร์ฟเวอร์แยกต่างหากเมื่อทำตามคำแนะนำเพื่อให้เซิร์ฟเวอร์ที่กำหนดไว้เป็นค่าเริ่มต้นเกินกว่าฉัน ไม่ว่าในกรณีใดหากมีการกำหนด default_server เฉพาะชุด Q / A นี้จะไม่ใช่สิ่งที่ควรพิจารณาเพื่อแก้ไขปัญหาใด ๆ ที่อาจเกิดขึ้น
Dayo

1
@Dayo คุณถูกต้องที่คุณระบุการตั้งค่าเฉพาะที่โพสต์โดย OP แต่สำหรับคำตอบสำหรับคำถามที่สมบูรณ์ฉันคิดว่าจำเป็นต้องพูดถึง default_server เป็นไปได้มากที่จะอ่านคำตอบของคุณและไม่ทราบว่า default_server จะรบกวนมันอย่างไร อาจเป็นไปได้มากขึ้นเนื่องจาก distros บางตัวจัดส่งด้วย default_server ที่กำหนดไว้ในไฟล์ที่อาจไม่ชัดเจนสำหรับผู้ใช้
Ben

61

คุณควรจะมีเซิร์ฟเวอร์เริ่มต้นสำหรับcatch-allคุณสามารถส่งคืน404หรือดีกว่าที่จะไม่ตอบสนองเลย (จะบันทึกแบนด์วิดท์บางส่วน) โดยการส่งกลับ444ซึ่งเป็นการตอบสนอง HTTP เฉพาะ nginx ที่ปิดการเชื่อมต่อและไม่คืนสิ่งใดเลย

server {
    listen       80  default_server;
    server_name  _; # some invalid name that won't match anything
    return       444;
}

ทำงานสำหรับฉันสำหรับ nginx 1.8.0 server_name _;ฉันไม่ได้มีในรุ่นก่อนหน้านี้สำหรับ Nginx แต่ทำงาน ตอนนี้สำหรับรุ่นใหม่ Nginx server_name _;ดูเหมือนว่าที่คุณต้องการ ขอบคุณ
daniel

แก้ไข ฉันลืมเครื่องหมายอัฒภาค; _;
user1201917

2
@iTech: ด้วยเหตุผลบางอย่างจะส่งคืน 444 สำหรับคำขอทั้งหมด เบาะแสใด ๆ
Divick

เคล็ดลับที่ดีเกี่ยวกับ444และเป็นวิธีที่สะอาดกว่าการส่งคืนรหัสข้อผิดพลาด
qqilihq

1
สิ่งสำคัญคือต้องระบุใบรับรอง / คีย์การเชื่อมต่อ SSL ทั้งหมดจะตรงกันและล้มเหลวตามที่ระบุโดย @AndreyT ในคำตอบของเขาด้านล่าง
Mark Fletcher

35

ฉันไม่สามารถแก้ไขปัญหาด้วยคำตอบอื่น ๆ ได้ ฉันแก้ไขปัญหาด้วยการตรวจสอบเพื่อดูว่าโฮสต์จับคู่และส่งคืน 403 ถ้าไม่ได้ (ฉันมีเว็บไซต์สุ่มที่ชี้ไปที่เนื้อหาเว็บเซิร์ฟเวอร์ของฉันฉันคาดว่าจะจี้อันดับการค้นหา)

server {
    listen 443;
    server_name example.com;

    if ($host != "example.com") {
        return 403;
    }

    ...
}

1
หากเป็นการปฏิบัติที่ไม่ดี: nginx.com/resources/wiki/start/topics/depth/ifisevil
Esolitos

1
มีหลายกรณีที่คุณไม่สามารถหลีกเลี่ยงการใช้ if ตัวอย่างเช่นถ้าคุณต้องการทดสอบตัวแปรที่ไม่มีคำสั่งเทียบเท่า
Edward

4
@Esolitos แท้จริงแล้วบรรทัดที่สามของบทความกล่าวว่ากรณีการใช้งานนี้ไม่เป็นไร ฉันเข้าใจถึงความจำเป็นในการใช้ความระมัดระวัง แต่ไม่ควรใช้มือในการใช้งานอย่างสมเหตุสมผล
mpowered

ในความคิดของฉันทางออกที่ง่ายและรัดกุมที่สุดเพราะมันต้องการเพียง 3 บรรทัดของโค้ดที่คุณสามารถวางลงในไฟล์ vhost ใด ๆ เพียงแค่เปลี่ยน $ host เปรียบเทียบหลังจากนั้น
Akito

28

เพื่อตอบคำถามของคุณ - nginx เลือกเซิร์ฟเวอร์แรกหากไม่มีตรงกัน ดูเอกสารประกอบ :

หากค่าไม่ตรงกับชื่อเซิร์ฟเวอร์ใด ๆ หรือการร้องขอไม่มีฟิลด์ส่วนหัวนี้เลย nginx จะกำหนดเส้นทางการร้องขอไปยังเซิร์ฟเวอร์เริ่มต้นสำหรับพอร์ตนี้ ในการกำหนดค่าข้างต้นเซิร์ฟเวอร์เริ่มต้นคือเซิร์ฟเวอร์แรก ...

ตอนนี้ถ้าคุณต้องการมีเซิร์ฟเวอร์ catch-all ที่เป็นค่าเริ่มต้นให้พูดตอบกลับด้วย 404 ต่อคำขอทั้งหมดแล้วนี่คือวิธีการ:

server {
    listen 80 default_server;
    listen 443 ssl default_server;
    server_name _;
    ssl_certificate <path to cert>
    ssl_certificate_key <path to key>
    return 404;
}

โปรดทราบว่าคุณต้องระบุใบรับรอง / คีย์ (ซึ่งสามารถลงนามด้วยตนเองได้) มิฉะนั้นการเชื่อมต่อ SSL ทั้งหมดจะล้มเหลวเนื่องจาก nginx จะพยายามยอมรับการเชื่อมต่อโดยใช้ default_server นี้และจะไม่พบใบรับรอง / คีย์


3
ฉันไม่รู้ว่าทำไมคำตอบนี้จึงอยู่ในรายการ นี่คือสิ่งที่ตอบคำถามโดยไม่ถูกรบกวนจากสิ่งที่แวววาวตลอดทาง
mmc

สิ่งนี้ช่วยให้ฉันแก้ปัญหาที่ฉันพยายามเปลี่ยนเส้นทางจากที่ไม่ใช่ www ไปยัง www ที่ฉันต้องรวมใบรับรอง SSL ของฉันในเส้นทางการเปลี่ยนเส้นทางนี้มิฉะนั้นพยายามดึง ssl Cert เริ่มต้นซึ่งเป็นโดเมนอื่น
endyourif

1
ดูเหมือนว่าจะเป็นคำตอบที่ดีที่สุดสำหรับการกำหนดค่าส่วนใหญ่ ... ไม่แน่ใจว่าใครกำลังใช้ nginx ที่ไม่มี SSL ในทุกวันนี้ แต่ความจริงที่ว่านี่เป็นคำเดียวที่ครอบคลุม SSL กำลังบอกได้อย่างมาก
mpowered

ดูเหมือนว่าserver_name _;ไม่จำเป็นแม้แต่
Julien Salinas

26

มีวิธีไม่กี่วิธีในการระบุเซิร์ฟเวอร์เริ่มต้น

วิธีแรก - ระบุเซิร์ฟเวอร์เริ่มต้นเป็นรายการแรกหากคุณเก็บการกำหนดค่าเซิร์ฟเวอร์ไว้ในไฟล์กำหนดค่าเดียวเช่น Dayo แสดงไว้ด้านบน

วิธีที่สอง (ดีกว่า)มีความยืดหยุ่นมากขึ้น - ระบุdefault_serverพารามิเตอร์สำหรับการlistenเรียนการสอนเช่น:

server {
    listen  *:80 default_server;
    root /www/project/public/;
}

ข้อมูลเพิ่มเติมที่นี่: Nginx doc / Listen

วิธีนี้มีประโยชน์มากขึ้นเมื่อคุณเก็บการกำหนดค่าเซิร์ฟเวอร์ไว้ในไฟล์แยกกันและไม่ต้องการตั้งชื่อไฟล์ตามลำดับตัวอักษร


3
นี่ควรเป็นคำตอบที่ถูกต้อง คำตอบที่ยอมรับนั้นทำให้เข้าใจผิด เพียงเพิ่มเซิร์ฟเวอร์อื่นในการกำหนดค่าจะไม่แก้ปัญหาหากเซิร์ฟเวอร์อื่นถูกกำหนดให้เป็นเซิร์ฟเวอร์เริ่มต้น
Ben

1
@Pavel ไม่มีเหตุผลว่าทำไมคำตอบที่ให้ไว้ไม่สามารถทำงานกับไฟล์ vhost แยกกันหลายไฟล์ ด้วยสิ่งนี้ฉันรู้ว่าเซิร์ฟเวอร์เริ่มต้นของฉันมักจะอยู่ในไฟล์ nginx.conf หลักและไม่ต้องจำว่าไฟล์ vhost ใดไฟล์หนึ่งที่แยกต่างหากของฉันมีสิ่งนี้
Dayo

อย่าลืมฟัง 443 ด้วยเช่นกันสำหรับ ssl (ดูคำตอบของ @ AndreyT ด้านล่าง)
Constantinos

8

ความคิดเห็นเล็กน้อยที่จะตอบ:

หากคุณมีโฮสต์เสมือนหลายแห่งในหลาย ๆ IP ในหลาย ๆ ไฟล์ในเว็บไซต์ที่มีอยู่ / กว่า "โดเมน" เริ่มต้นสำหรับ IP จะถูกนำมาจากไฟล์แรกตามลำดับตัวอักษร

และดังที่ Pavel กล่าวว่ามีอาร์กิวเมนต์ "default_server" สำหรับคำสั่ง "ฟัง" http://nginx.org/en/docs/http/ngx_http_core_module.html#listen

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.