PHP로 뭔가를 만들다 보면 "지금 이 요청이 어디서, 어떤 주소로 들어왔는지"를 알아야 할 때가 자주 옵니다. 접속 로그를 남기거나, 현재 페이지 주소를 기준으로 링크를 만들거나, GET/POST에 따라 처리를 나눌 때가 다 그렇죠. 이런 정보는 전부 $_SERVER 슈퍼글로벌 배열에 들어 있습니다.
아주 오래된 코드를 보면 같은 용도로 $HTTP_SERVER_VARS를 쓰는 경우가 있습니다. 이건 예전 방식이라 폐기(deprecated)됐다가 PHP 5.4에서 아예 없어졌어요. 옛 소스를 옮겨 올 때 이 변수가 보이면 $_SERVER로 바꿔 주면 됩니다. 비슷한 정보를 함수로 꺼내는 getenv()도 있지만, 웹 요청 정보는 $_SERVER를 쓰는 쪽이 일반적입니다.
자주 쓰는 키를 한 번에 찍어 보기
키 이름을 매번 찾아보기 귀찮아서, 자주 쓰는 것만 배열에 넣고 표로 출력하는 테스트 파일을 하나 만들어 두면 편합니다. 값이 없는 키는 -로 표시합니다.
<?php
// 자주 사용하는 것만... 그 외는 php.net에서 확인
// http://localhost/testphp/11.php?test=paramiter&test2=para2
// 위 URL 접속시 나오는 정보입니다.
$indicesServer = array(
'PHP_SELF', // 현재 접속 주소(도메인 제외)
'SERVER_ADDR', // 서버 IP
'SERVER_NAME', // 서버 네임
'REQUEST_METHOD', // 요청 방식
'QUERY_STRING', // URL 에 있는 파라미터 반환
'DOCUMENT_ROOT', // 서버의 아파치 루트 디렉토리
'HTTP_ACCEPT', // 문서 구성 및 타입 헤더 내용
'HTTP_ACCEPT_CHARSET', // 캐릭터셋 헤더 내용
'HTTP_ACCEPT_ENCODING', // 인코딩 방식 헤더 내용
'HTTP_ACCEPT_LANGUAGE', // 언어 헤더 내용
'HTTP_HOST', // 현재 도메인
'HTTP_REFERER', // 현재 페이지로 오기 전 페이지 URL
'HTTP_USER_AGENT', // 현재 페이지 접속한 사용자 환경
'REMOTE_ADDR', // 현재 페이지 접속한 사용자 IP
'REMOTE_HOST', // 현재 페이지 접속한 사용자 호스트
'REMOTE_PORT', // 현재 페이지 접속한 사용자 포트
'SCRIPT_FILENAME', // 접속 중인 사이트의 파일명과 경로
'SERVER_PORT', // 접속 중인 사이트의 포트
'REQUEST_URI' // 현재 페이지의 URL
) ;
echo '<table cellpadding="10">' ;
foreach ($indicesServer as $arg){
if (isset($_SERVER[$arg])){
echo '<tr><td>'.$arg.'</td><td>'.
$_SERVER[$arg].'</td></tr>';
}
else{
echo '<tr><td>'.$arg.'</td><td>-</td></tr>';
}
} // foreach 문 끝
echo '</table>' ;
?>
실제로 찍어 본 값
http://localhost/testphp/11.php?test=paramiter&test2=para2로 접속했을 때 나온 결과입니다. 로컬 아파치 환경 기준이라 경로나 IP는 환경마다 다릅니다.
| 키 | 출력값 |
|---|---|
| PHP_SELF | /testphp/11.php |
| SERVER_ADDR | 127.0.0.1 |
| SERVER_NAME | localhost |
| REQUEST_METHOD | GET |
| QUERY_STRING | test=paramiter&test2=para2 |
| DOCUMENT_ROOT | /www/ |
| HTTP_ACCEPT | text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 |
| HTTP_ACCEPT_CHARSET | - |
| HTTP_ACCEPT_ENCODING | gzip,deflate,sdch |
| HTTP_ACCEPT_LANGUAGE | ko-KR,ko;q=0.8,en-US;q=0.6,en;q=0.4 |
| HTTP_HOST | localhost |
| HTTP_REFERER | - |
| HTTP_USER_AGENT | Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36 |
| REMOTE_ADDR | 192.168.0.1 |
| REMOTE_HOST | - |
| REMOTE_PORT | 55429 |
| SCRIPT_FILENAME | /www/testphp/11.php |
| SERVER_PORT | 80 |
| REQUEST_URI | /testphp/11.php?test=paramiter&test2=para2 |
표를 보면 헷갈리기 쉬운 것들이 눈에 들어옵니다. PHP_SELF는 스크립트 경로만, REQUEST_URI는 쿼리스트링까지 포함한 주소 전체를, QUERY_STRING은 ? 뒤만 돌려줍니다. 현재 페이지로 되돌아오는 링크를 만들 때 파라미터를 유지하고 싶으면 REQUEST_URI, 파라미터 없이 파일 경로만 필요하면 PHP_SELF(또는 SCRIPT_NAME)를 쓰는 식으로 구분합니다.
HTTP_REFERER는 주소창에 직접 쳐서 들어오면 비어 있어서 -로 나왔고, REMOTE_HOST도 비어 있는데 이건 웹서버가 IP를 호스트명으로 역조회하도록 설정돼 있어야 채워지기 때문입니다. 대부분의 서버는 그 설정을 꺼 둡니다. HTTP_ACCEPT_CHARSET은 요즘 브라우저가 거의 보내지 않는 헤더라 비어 있는 게 정상입니다.
믿으면 안 되는 값들
HTTP_로 시작하는 키는 전부 브라우저가 보낸 요청 헤더를 그대로 옮긴 것입니다. 클라이언트가 마음대로 바꿔 보낼 수 있다는 뜻이죠. 리퍼러로 "우리 사이트에서 온 요청만 허용"하는 식의 검사는 보안 장치가 못 되고, HTTP_HOST도 조작될 수 있어서 비밀번호 재설정 메일의 링크 주소 같은 데 그대로 쓰면 곤란합니다. 그나마 REMOTE_ADDR은 TCP 연결 기준이라 조작이 어렵지만, 앞단에 프록시나 로드밸런서가 있으면 프록시의 IP가 찍힙니다.
그리고 위 예제는 값을 그대로 echo하고 있어서, 쿼리스트링이나 User-Agent에 스크립트를 넣어 접속하면 화면에 그대로 실행될 수 있습니다. 혼자 보는 테스트 파일이면 상관없지만, 관리자 화면 같은 데 붙일 거라면 출력할 때 htmlspecialchars()로 감싸 주세요.
<?php
// 출력 부분만 이스케이프를 추가한 버전
foreach ($indicesServer as $arg){
$val = isset($_SERVER[$arg]) ? htmlspecialchars($_SERVER[$arg], ENT_QUOTES, 'UTF-8') : '-';
echo '<tr><td>'.$arg.'</td><td>'.$val.'</td></tr>';
}
?>
여기 없는 키가 궁금하면 print_r($_SERVER); 한 줄로 전체를 찍어 보면 됩니다. 아파치냐 nginx+PHP-FPM이냐, CLI로 돌리느냐에 따라 들어 있는 키가 꽤 달라서, 서버를 옮길 때 한 번씩 확인해 두면 나중에 덜 헤맵니다.
참고: 로이데스트 - $_SERVER 접속자와 접속 페이지 관련 정보 반환