2025年11月11日火曜日

APEXおよびORDSが認識するリクエストの送信元を確認する

HTTPSのリバース・プロキシを構成する際に、Oracle REST Data ServicesおよびAPEXが認識するリクエストの送信元を確認した手順を紹介します。

確認作業ではmacOSのpodmanで、コンテナとして実行しているOracle APEXの環境を使用しています。

最初に以下のようにHTTPで通信する、基本的な構成で動作を確認します。ORDSはポート8080でHTTPのリクエストを受け付けますが、ホスト・ポート8080にコンテナ・ポート8181を割り当てているため、外部からはhttp://localhost:8181/を宛先として、ORDSおよびAPEXにアクセスします。
APEXにワークスペースAPEXDEV(スキーマWKSP_APEXDEV)が作成済みとします。

動作確認に使用するORDS RESTサービスを作成します。

APEXのワークスペース・スキーマに接続し、以下のコードを実行します。/debug/print-http-headersの呼び出しでowa_util.print_cgi_envを実行し、ORDS(およびAPEX)が受信したHTTPヘッダーを印刷します。


次にAutoRESTのサービスを実装します。表TEST_PAGINGを作成します。
create table test_paging (
    id               number generated by default on null as identity
                     constraint test_paging_id_pk primary key,
    unique_string    varchar2(32 char)
);

SQL> create table test_paging (

  2      id               number generated by default on null as identity

  3                       constraint test_paging_id_pk primary key,

  4      unique_string    varchar2(32 char)

  5* );


Table TEST_PAGINGは作成されました。


SQL> 


表TEST_PAGINGに任意の行を50行投入します。
begin
for i in 1..50
loop
    insert into test_paging(unique_string) values(rawtohex(sys_guid()));
end loop;
commit;
end;
/

SQL> begin

  2  for i in 1..50

  3  loop

  4      insert into test_paging(unique_string) values(rawtohex(sys_guid()));

  5  end loop;

  6  commit;

  7  end;

  8* /


PL/SQLプロシージャが正常に完了しました。


SQL> 


表TEST_PAGINGをAutoREST有効にします。
begin
    ords.enable_object(
        p_enabled => true,
        p_object => 'TEST_PAGING'
    );
end;
/

SQL> begin

  2      ords.enable_object(

  3          p_enabled => true,

  4          p_object => 'TEST_PAGING'

  5      );

  6  end;

  7* /


PL/SQLプロシージャが正常に完了しました。


SQL> 


以上で確認作業の準備ができました。

HTTPで直接ORDSにアクセスし、ORDSのRESTサービスとAPEXを呼び出してみます。動作確認にcurlコマンドを使用します。

最初にAPEXを呼び出します。

curl -v "http://localhost:8181/ords/apex"

% curl -v "http://localhost:8181/ords/apex"     

* Host localhost:8181 was resolved.

* IPv6: ::1

* IPv4: 127.0.0.1

*   Trying [::1]:8181...

* Connected to localhost (::1) port 8181

> GET /ords/apex HTTP/1.1

> Host: localhost:8181

> User-Agent: curl/8.7.1

> Accept: */*

> 

* Request completely sent off

< HTTP/1.1 302 Found

< Content-Type: text/html;charset=utf-8

< Trailer: APEX_APP_ID

< X-Content-Type-Options: nosniff

< X-Xss-Protection: 1; mode=block

< Referrer-Policy: strict-origin

< Cache-Control: no-store

< Pragma: no-cache

< Expires: Sun, 27 Jul 1997 13:00:00 GMT

< Set-Cookie: ORA_WWV_USER_1667447080439607=ORA_WWV-B-g8HzHF0HQImIYTub7gBT4R; path=/ords/; HttpOnly

< Location: http://localhost:8181/ords/r/apex/workspace-sign-in/oracle-apex-sign-in?session=27980393712644

< Transfer-Encoding: chunked

< 

* Connection #0 to host localhost left intact

% 


/ords/apexにアクセスすると、ワークスペースへのサインイン・ページを表示するため、APEXとして認識したクライアント(リクエストの送信元)に、サインイン・ページにリダイレクトするようレスポンスを返します。

上記ではステータス・コードとして302、Locationヘッダーに以下を戻しています。

Location: http://localhost:8181/ords/r/apex/workspace-sign-in/oracle-apex-sign-in?session=27980393712644

ちなみに/ords/apexのapexはデータベースに作成されたプロシージャです。以下のSELECT文でプロシージャAPEXの存在を確認できます。ORDSは/ords/apexを受け付けて、プロシージャAPEXを呼び出しています。

SQL> select owner, object_type, object_name from dba_objects where object_name = 'APEX';


OWNER          OBJECT_TYPE    OBJECT_NAME    

______________ ______________ ______________ 

PUBLIC         SYNONYM        APEX           

APEX_240200    PROCEDURE      APEX           


SQL> 


この構成の場合、リクエストの送信元はHostヘッダーで決まるようです。curlコマンドでHostヘッダーを上書きすると、リダイレクト先はHostヘッダーで指定した宛先になります。

curl -H "Host: example.com:9999" -v "http://localhost:8181/ords/apex"

% curl -H "Host: example.com:9999" -v "http://localhost:8181/ords/apex"

* Host localhost:8181 was resolved.

* IPv6: ::1

* IPv4: 127.0.0.1

*   Trying [::1]:8181...

* Connected to localhost (::1) port 8181

> GET /ords/apex HTTP/1.1

> Host: example.com:9999

> User-Agent: curl/8.7.1

> Accept: */*

> 

* Request completely sent off

< HTTP/1.1 302 Found

< Content-Type: text/html;charset=utf-8

< Trailer: APEX_APP_ID

< X-Content-Type-Options: nosniff

< X-Xss-Protection: 1; mode=block

< Referrer-Policy: strict-origin

< Cache-Control: no-store

< Pragma: no-cache

< Expires: Sun, 27 Jul 1997 13:00:00 GMT

< Set-Cookie: ORA_WWV_USER_1667447080439607=ORA_WWV-bXQDt6ql6q6RvuuQ_UMOzM3g; path=/ords/; HttpOnly

< Location: http://example.com:9999/ords/r/apex/workspace-sign-in/oracle-apex-sign-in?session=20233391851662

< Transfer-Encoding: chunked

< 

* Connection #0 to host localhost left intact

% 


ORDSが受信しているHTTPヘッダーを出力します。

curl "http://localhost:8181/ords/apexdev/debug/print-http-headers"

この中の、REQUEST_SCHEMESERVER_NAMESERVER_PORTがリダイレクト先を決めているように見受けられます。

% curl "http://localhost:8181/ords/apexdev/debug/print-http-headers"

REMOTE_IDENT = <br />

REMOTE_USER = WKSP_APEXDEV<br />

Host = localhost:8181<br />

User-Agent = curl/8.7.1<br />

Accept = */*<br />

ORDS_VERSION = 25.3.1.r2891312<br />

APEX_LISTENER_VERSION = 25.3.1.r2891312<br />

DAD_NAME = <br />

DOC_ACCESS_PATH = <br />

DOCUMENT_TABLE = <br />

GATEWAY_IVERSION = 3<br />

GATEWAY_INTERFACE = CGI/1.1<br />

HTTP_ACCEPT = */*<br />

HTTP_ACCEPT_ENCODING = <br />

HTTP_ACCEPT_LANGUAGE = <br />

HTTP_ACCEPT_CHARSET = <br />

HTTP_IF_MODIFIED_SINCE = <br />

HTTP_IF_NONE_MATCH = <br />

HTTP_HOST = localhost:8181<br />

HTTP_ORACLE_ECID = <br />

HTTP_PORT = 8181<br />

HTTP_REFERER = <br />

HTTP_USER_AGENT = curl/8.7.1<br />

PATH_ALIAS = <br />

PATH_INFO = /print-http-headers<br />

PLSQL_GATEWAY = WebDb<br />

QUERY_STRING = <br />

REMOTE_ADDR = 10.89.0.5<br />

REQUEST_CHARSET = AL32UTF8<br />

REQUEST_CONTEXT_PATH = /ords/apexdev<br />

REQUEST_CONTEXT_ROOT = /ords<br />

REQUEST_IANA_CHARSET = UTF-8<br />

REQUEST_METHOD = GET<br />

REQUEST_PROTOCOL = http<br />

REQUEST_SCHEME = http<br />

SCRIPT_NAME = /ords/apexdev/debug<br />

SCRIPT_PREFIX = <br />

SERVER_NAME = localhost<br />

SERVER_PORT = 8181<br />

SERVER_PROTOCOL = HTTP/1.1<br />

SERVER_SOFTWARE = Mod-Apex<br />

WEB_AUTHENT_PREFIX = <br />

X-APEX-METHOD = GET<br />

X-APEX-BASE = http://localhost:8181/ords/apexdev/<br />

X-APEX-PATH = debug/print-http-headers<br />

X-APEX-REMOTE-ADDRESS = 10.89.0.5<br />

X-APEX-CHARSET = UTF-8<br />

HTTP_COOKIE = <br />

%


ORDSが認識しているリクエストの送信元を確認します。AutoRESTを呼び出します。

curl "http://localhost:8181/ords/apexdev/test_paging/?limit=2" | jq

ページング指定にfirstやnextがあります。これらのリクエストの宛先が、Hostヘッダーで決まるhttp://localhost:8181/になっています。

% curl "http://localhost:8181/ords/apexdev/test_paging/?limit=2" | jq

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current

                                 Dload  Upload   Total   Spent    Left  Speed

100   767    0   767    0     0  29542      0 --:--:-- --:--:-- --:--:-- 30680

{

  "items": [

    {

      "id": 51,

      "unique_string": "4349E9E4AE998959E0630500590AA658",

      "links": [

        {

          "rel": "self",

          "href": "http://localhost:8181/ords/apexdev/test_paging/51"

        }

      ]

    },

    {

      "id": 52,

      "unique_string": "4349E9E4AE9A8959E0630500590AA658",

      "links": [

        {

          "rel": "self",

          "href": "http://localhost:8181/ords/apexdev/test_paging/52"

        }

      ]

    }

  ],

  "hasMore": true,

  "limit": 2,

  "offset": 0,

  "count": 2,

  "links": [

    {

      "rel": "self",

      "href": "http://localhost:8181/ords/apexdev/test_paging/"

    },

    {

      "rel": "edit",

      "href": "http://localhost:8181/ords/apexdev/test_paging/"

    },

    {

      "rel": "describedby",

      "href": "http://localhost:8181/ords/apexdev/metadata-catalog/test_paging/"

    },

    {

      "rel": "first",

      "href": "http://localhost:8181/ords/apexdev/test_paging/?limit=2"

    },

    {

      "rel": "next",

      "href": "http://localhost:8181/ords/apexdev/test_paging/?offset=2&limit=2"

    }

  ]

}

% 


Hostヘッダーを置き換えて呼び出してみます。

curl -H "Host: example.com:9999" "http://localhost:8181/ords/apexdev/test_paging/?limit=2" | jq

リクエストした行(limit=2なので2行)は返されますが、ページング先のURLはHostヘッダーを反映して、http://example.com:9999/になっています。そのため、このレスポンスから次のページにアクセスしようするとエラーが発生します。

% curl -H "Host: example.com:9999" "http://localhost:8181/ords/apexdev/test_paging/?limit=2" | jq

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current

                                 Dload  Upload   Total   Spent    Left  Speed

100   781    0   781    0     0  38596      0 --:--:-- --:--:-- --:--:-- 39050

{

  "items": [

    {

      "id": 51,

      "unique_string": "4349E9E4AE998959E0630500590AA658",

      "links": [

        {

          "rel": "self",

          "href": "http://example.com:9999/ords/apexdev/test_paging/51"

        }

      ]

    },

    {

      "id": 52,

      "unique_string": "4349E9E4AE9A8959E0630500590AA658",

      "links": [

        {

          "rel": "self",

          "href": "http://example.com:9999/ords/apexdev/test_paging/52"

        }

      ]

    }

  ],

  "hasMore": true,

  "limit": 2,

  "offset": 0,

  "count": 2,

  "links": [

    {

      "rel": "self",

      "href": "http://example.com:9999/ords/apexdev/test_paging/"

    },

    {

      "rel": "edit",

      "href": "http://example.com:9999/ords/apexdev/test_paging/"

    },

    {

      "rel": "describedby",

      "href": "http://example.com:9999/ords/apexdev/metadata-catalog/test_paging/"

    },

    {

      "rel": "first",

      "href": "http://example.com:9999/ords/apexdev/test_paging/?limit=2"

    },

    {

      "rel": "next",

      "href": "http://example.com:9999/ords/apexdev/test_paging/?offset=2&limit=2"

    }

  ]

}

% 


この構成に加えて、ORDSのフロントにNginxで構成したリバース・プロキシを配置します。NginxはHTTPSでリクエストを受け付け、バックエンドへのORDSにはHTTPでアクセスします。


Nginxの/etc/nginx/conf.d/default.confには以下の設定を使用します。本来であればパス /i/ についても /ords/ と同様の設定が必要ですが、今回は動作確認だけができればよいので割愛しています。


この構成で、curlコマンドでぞれぞれのURLにアクセスします。

APEXにリクエストします。

curl -v -k "https://localhost:9443/ords/apex"

% curl -v -k "https://localhost:9443/ords/apex"

]* Host localhost:9443 was resolved.

* IPv6: ::1

* IPv4: 127.0.0.1

*   Trying [::1]:9443...

* Connected to localhost (::1) port 9443

* ALPN: curl offers h2,http/1.1

* (304) (OUT), TLS handshake, Client hello (1):

* (304) (IN), TLS handshake, Server hello (2):

* (304) (IN), TLS handshake, Unknown (8):

* (304) (IN), TLS handshake, Certificate (11):

* (304) (IN), TLS handshake, CERT verify (15):

* (304) (IN), TLS handshake, Finished (20):

* (304) (OUT), TLS handshake, Finished (20):

* SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256 / [blank] / UNDEF

* ALPN: server accepted http/1.1

* Server certificate:

*  subject: C=JP; ST=Tokyo; L=Chiyoda; O=LocalDev; OU=IT; CN=localhost

*  start date: Nov 10 00:53:57 2025 GMT

*  expire date: Nov 10 00:53:57 2026 GMT

*  issuer: C=JP; ST=Tokyo; L=Chiyoda; O=LocalDev; OU=IT; CN=localhost

*  SSL certificate verify result: self signed certificate (18), continuing anyway.

* using HTTP/1.x

> GET /ords/apex HTTP/1.1

> Host: localhost:9443

> User-Agent: curl/8.7.1

> Accept: */*

> 

* Request completely sent off

< HTTP/1.1 302 Found

< Server: nginx/1.29.3

< Date: Tue, 11 Nov 2025 04:02:46 GMT

< Content-Type: text/html;charset=utf-8

< Transfer-Encoding: chunked

< Connection: keep-alive

< Trailer: APEX_APP_ID

< X-Content-Type-Options: nosniff

< X-Xss-Protection: 1; mode=block

< Referrer-Policy: strict-origin

< Cache-Control: no-store

< Pragma: no-cache

< Expires: Sun, 27 Jul 1997 13:00:00 GMT

< Set-Cookie: ORA_WWV_USER_1667447080439607=ORA_WWV-6Y9nfYF74jzkAJHzeBiKj5S3; path=/ords/; secure; HttpOnly

< Location: http://localhost:9443/ords/r/apex/workspace-sign-in/oracle-apex-sign-in?session=9268568934599

< 

* Connection #0 to host localhost left intact

ynakakoshi@Ns-Macbook apex-podman-setup % curl -v -k "https://localhost:9443/ords/apex"

* Host localhost:9443 was resolved.

* IPv6: ::1

* IPv4: 127.0.0.1

*   Trying [::1]:9443...

* Connected to localhost (::1) port 9443

* ALPN: curl offers h2,http/1.1

* (304) (OUT), TLS handshake, Client hello (1):

* (304) (IN), TLS handshake, Server hello (2):

* (304) (IN), TLS handshake, Unknown (8):

* (304) (IN), TLS handshake, Certificate (11):

* (304) (IN), TLS handshake, CERT verify (15):

* (304) (IN), TLS handshake, Finished (20):

* (304) (OUT), TLS handshake, Finished (20):

* SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256 / [blank] / UNDEF

* ALPN: server accepted http/1.1

* Server certificate:

*  subject: C=JP; ST=Tokyo; L=Chiyoda; O=LocalDev; OU=IT; CN=localhost

*  start date: Nov 10 00:53:57 2025 GMT

*  expire date: Nov 10 00:53:57 2026 GMT

*  issuer: C=JP; ST=Tokyo; L=Chiyoda; O=LocalDev; OU=IT; CN=localhost

*  SSL certificate verify result: self signed certificate (18), continuing anyway.

* using HTTP/1.x

> GET /ords/apex HTTP/1.1

> Host: localhost:9443

> User-Agent: curl/8.7.1

> Accept: */*

> 

* Request completely sent off

< HTTP/1.1 302 Found

< Server: nginx/1.29.3

< Date: Tue, 11 Nov 2025 04:02:58 GMT

< Content-Type: text/html;charset=utf-8

< Transfer-Encoding: chunked

< Connection: keep-alive

< Trailer: APEX_APP_ID

< X-Content-Type-Options: nosniff

< X-Xss-Protection: 1; mode=block

< Referrer-Policy: strict-origin

< Cache-Control: no-store

< Pragma: no-cache

< Expires: Sun, 27 Jul 1997 13:00:00 GMT

< Set-Cookie: ORA_WWV_USER_1667447080439607=ORA_WWV-DTglRt8kXvVQD6xevX0Fn7Jn; path=/ords/; secure; HttpOnly

< Location: http://localhost:9443/ords/r/apex/workspace-sign-in/oracle-apex-sign-in?session=11431697437258

< 

* Connection #0 to host localhost left intact

% 


リダイレクト先となるLocationヘッダーを見ると、httpsではなくhttpになっています。そのため、この構成ではAPEXにアクセスできません。

Location: http://localhost:9443/ords/r/apex/workspace-sign-in/oracle-apex-sign-in?session=11431697437258

ORDSが受信したヘッダーを確認します。

curl -k "https://localhost:9443/ords/apexdev/debug/print-http-headers"

REQUEST_SCHEMEhttpとなっています。そのため、リダイレクト先のプロトコルがhttpになります。

% curl -k "https://localhost:9443/ords/apexdev/debug/print-http-headers"

REMOTE_IDENT = <br />

REMOTE_USER = WKSP_APEXDEV<br />

Host = localhost:9443<br />

X-Forwarded-Host = localhost:9443<br />

X-Real-IP = 10.89.0.5<br />

X-Forwarded-For = 10.89.0.5<br />

X-Forwarded-Proto = https<br />

Connection = close<br />

User-Agent = curl/8.7.1<br />

Accept = */*<br />

ORDS_VERSION = 25.3.1.r2891312<br />

APEX_LISTENER_VERSION = 25.3.1.r2891312<br />

DAD_NAME = <br />

DOC_ACCESS_PATH = <br />

DOCUMENT_TABLE = <br />

GATEWAY_IVERSION = 3<br />

GATEWAY_INTERFACE = CGI/1.1<br />

HTTP_ACCEPT = */*<br />

HTTP_ACCEPT_ENCODING = <br />

HTTP_ACCEPT_LANGUAGE = <br />

HTTP_ACCEPT_CHARSET = <br />

HTTP_IF_MODIFIED_SINCE = <br />

HTTP_IF_NONE_MATCH = <br />

HTTP_HOST = localhost:9443<br />

HTTP_ORACLE_ECID = <br />

HTTP_PORT = 9443<br />

HTTP_REFERER = <br />

HTTP_USER_AGENT = curl/8.7.1<br />

PATH_ALIAS = <br />

PATH_INFO = /print-http-headers<br />

PLSQL_GATEWAY = WebDb<br />

QUERY_STRING = <br />

REMOTE_ADDR = [0:0:0:0:0:0:0:1]<br />

REQUEST_CHARSET = AL32UTF8<br />

REQUEST_CONTEXT_PATH = /ords/apexdev<br />

REQUEST_CONTEXT_ROOT = /ords<br />

REQUEST_IANA_CHARSET = UTF-8<br />

REQUEST_METHOD = GET<br />

REQUEST_PROTOCOL = http<br />

REQUEST_SCHEME = http<br />

SCRIPT_NAME = /ords/apexdev/debug<br />

SCRIPT_PREFIX = <br />

SERVER_NAME = localhost<br />

SERVER_PORT = 9443<br />

SERVER_PROTOCOL = HTTP/1.1<br />

SERVER_SOFTWARE = Mod-Apex<br />

WEB_AUTHENT_PREFIX = <br />

X-APEX-METHOD = GET<br />

X-APEX-BASE = http://localhost:9443/ords/apexdev/<br />

X-APEX-PATH = debug/print-http-headers<br />

X-APEX-REMOTE-ADDRESS = [0:0:0:0:0:0:0:1]<br />

X-APEX-CHARSET = UTF-8<br />

HTTP_COOKIE = <br />

% 


AutoRESTを呼び出してみます。

curl -k "https://localhost:9443/ords/apexdev/test_paging/?limit=2" | jq

一見適切に呼び出せているように見えますが、linksに含まれるfirstnextの宛先がhttpになっているため、ページングなどの処理は失敗します。

% curl -k "https://localhost:9443/ords/apexdev/test_paging/?limit=2" | jq


  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current

                                 Dload  Upload   Total   Spent    Left  Speed

100   767    0   767    0     0  12791      0 --:--:-- --:--:-- --:--:-- 13000

{

  "items": [

    {

      "id": 51,

      "unique_string": "4349E9E4AE998959E0630500590AA658",

      "links": [

        {

          "rel": "self",

          "href": "http://localhost:9443/ords/apexdev/test_paging/51"

        }

      ]

    },

    {

      "id": 52,

      "unique_string": "4349E9E4AE9A8959E0630500590AA658",

      "links": [

        {

          "rel": "self",

          "href": "http://localhost:9443/ords/apexdev/test_paging/52"

        }

      ]

    }

  ],

  "hasMore": true,

  "limit": 2,

  "offset": 0,

  "count": 2,

  "links": [

    {

      "rel": "self",

      "href": "http://localhost:9443/ords/apexdev/test_paging/"

    },

    {

      "rel": "edit",

      "href": "http://localhost:9443/ords/apexdev/test_paging/"

    },

    {

      "rel": "describedby",

      "href": "http://localhost:9443/ords/apexdev/metadata-catalog/test_paging/"

    },

    {

      "rel": "first",

      "href": "http://localhost:9443/ords/apexdev/test_paging/?limit=2"

    },

    {

      "rel": "next",

      "href": "http://localhost:9443/ords/apexdev/test_paging/?offset=2&limit=2"

    }

  ]

}

% 


ORDSのプロパティsecurity.httpsHeaderCheckX-Forwarded-Proto: httpsを設定し、リクエストのプロコトルをhttpsとして認識させます。

ords --config /etc/ords/config config set security.httpsHeaderCheck "X-Forwarded-Proto: https"

再度、動作確認します。

APEXにリクエストします。

curl -v -k "https://localhost:9443/ords/apex"

% curl -v -k "https://localhost:9443/ords/apex"


* Host localhost:9443 was resolved.

* IPv6: ::1

* IPv4: 127.0.0.1

*   Trying [::1]:9443...

* Connected to localhost (::1) port 9443

* ALPN: curl offers h2,http/1.1

* (304) (OUT), TLS handshake, Client hello (1):

* (304) (IN), TLS handshake, Server hello (2):

* (304) (IN), TLS handshake, Unknown (8):

* (304) (IN), TLS handshake, Certificate (11):

* (304) (IN), TLS handshake, CERT verify (15):

* (304) (IN), TLS handshake, Finished (20):

* (304) (OUT), TLS handshake, Finished (20):

* SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256 / [blank] / UNDEF

* ALPN: server accepted http/1.1

* Server certificate:

*  subject: C=JP; ST=Tokyo; L=Chiyoda; O=LocalDev; OU=IT; CN=localhost

*  start date: Nov 10 00:53:57 2025 GMT

*  expire date: Nov 10 00:53:57 2026 GMT

*  issuer: C=JP; ST=Tokyo; L=Chiyoda; O=LocalDev; OU=IT; CN=localhost

*  SSL certificate verify result: self signed certificate (18), continuing anyway.

* using HTTP/1.x

> GET /ords/apex HTTP/1.1

> Host: localhost:9443

> User-Agent: curl/8.7.1

> Accept: */*

> 

* Request completely sent off

< HTTP/1.1 302 Found

< Server: nginx/1.29.3

< Date: Tue, 11 Nov 2025 04:24:34 GMT

< Content-Type: text/html;charset=utf-8

< Transfer-Encoding: chunked

< Connection: keep-alive

< Trailer: APEX_APP_ID

< X-Content-Type-Options: nosniff

< X-Xss-Protection: 1; mode=block

< Referrer-Policy: strict-origin

< Cache-Control: no-store

< Pragma: no-cache

< Expires: Sun, 27 Jul 1997 13:00:00 GMT

< Set-Cookie: ORA_WWV_USER_1667447080439607=ORA_WWV-gpm-Ciact0B5stHn15WZqRS4; path=/ords/; secure; HttpOnly

< Location: https://localhost/ords/r/apex/workspace-sign-in/oracle-apex-sign-in?session=17153398914728

< 

* Connection #0 to host localhost left intact

% 


リダイレクト先となるLocationはhttpsに代わりましたが、ポート番号の9443が削除され標準ポート(443)がリダイレクト先になっています。

ORDSが受信したヘッダーを確認します。

curl -k "https://localhost:9443/ords/apexdev/debug/print-http-headers"

REQUEST_SCHEMEhttpsになっていますが、SERVER_PORT443と認識されています。

% curl -k "https://localhost:9443/ords/apexdev/debug/print-http-headers"

REMOTE_IDENT = <br />

REMOTE_USER = WKSP_APEXDEV<br />

Host = localhost:9443<br />

X-Forwarded-Host = localhost:9443<br />

X-Real-IP = 10.89.0.5<br />

X-Forwarded-For = 10.89.0.5<br />

X-Forwarded-Proto = https<br />

Connection = close<br />

User-Agent = curl/8.7.1<br />

Accept = */*<br />

ORDS_VERSION = 25.3.1.r2891312<br />

APEX_LISTENER_VERSION = 25.3.1.r2891312<br />

DAD_NAME = <br />

DOC_ACCESS_PATH = <br />

DOCUMENT_TABLE = <br />

GATEWAY_IVERSION = 3<br />

GATEWAY_INTERFACE = CGI/1.1<br />

HTTP_ACCEPT = */*<br />

HTTP_ACCEPT_ENCODING = <br />

HTTP_ACCEPT_LANGUAGE = <br />

HTTP_ACCEPT_CHARSET = <br />

HTTP_IF_MODIFIED_SINCE = <br />

HTTP_IF_NONE_MATCH = <br />

HTTP_HOST = localhost<br />

HTTP_ORACLE_ECID = <br />

HTTP_PORT = 443<br />

HTTP_REFERER = <br />

HTTP_USER_AGENT = curl/8.7.1<br />

PATH_ALIAS = <br />

PATH_INFO = /print-http-headers<br />

PLSQL_GATEWAY = WebDb<br />

QUERY_STRING = <br />

REMOTE_ADDR = [0:0:0:0:0:0:0:1]<br />

REQUEST_CHARSET = AL32UTF8<br />

REQUEST_CONTEXT_PATH = /ords/apexdev<br />

REQUEST_CONTEXT_ROOT = /ords<br />

REQUEST_IANA_CHARSET = UTF-8<br />

REQUEST_METHOD = GET<br />

REQUEST_PROTOCOL = https<br />

REQUEST_SCHEME = https<br />

SCRIPT_NAME = /ords/apexdev/debug<br />

SCRIPT_PREFIX = <br />

SERVER_NAME = localhost<br />

SERVER_PORT = 443<br />

SERVER_PROTOCOL = HTTP/1.1<br />

SERVER_SOFTWARE = Mod-Apex<br />

WEB_AUTHENT_PREFIX = <br />

X-APEX-METHOD = GET<br />

X-APEX-BASE = https://localhost/ords/apexdev/<br />

X-APEX-PATH = debug/print-http-headers<br />

X-APEX-REMOTE-ADDRESS = [0:0:0:0:0:0:0:1]<br />

X-APEX-CHARSET = UTF-8<br />

HTTP_COOKIE = <br />

% 


AutoRESTを呼び出してみます。

curl -k "https://localhost:9443/ords/apexdev/test_paging/?limit=2" | jq

AutoRESTのlinksに含まれるURLも、プロコトルはhttpsですが、ポート番号は標準ポートになっています。

% curl -k "https://localhost:9443/ords/apexdev/test_paging/?limit=2" | jq


  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current

                                 Dload  Upload   Total   Spent    Left  Speed

100   739    0   739    0     0   6339      0 --:--:-- --:--:-- --:--:--  6370

{

  "items": [

    {

      "id": 51,

      "unique_string": "4349E9E4AE998959E0630500590AA658",

      "links": [

        {

          "rel": "self",

          "href": "https://localhost/ords/apexdev/test_paging/51"

        }

      ]

    },

    {

      "id": 52,

      "unique_string": "4349E9E4AE9A8959E0630500590AA658",

      "links": [

        {

          "rel": "self",

          "href": "https://localhost/ords/apexdev/test_paging/52"

        }

      ]

    }

  ],

  "hasMore": true,

  "limit": 2,

  "offset": 0,

  "count": 2,

  "links": [

    {

      "rel": "self",

      "href": "https://localhost/ords/apexdev/test_paging/"

    },

    {

      "rel": "edit",

      "href": "https://localhost/ords/apexdev/test_paging/"

    },

    {

      "rel": "describedby",

      "href": "https://localhost/ords/apexdev/metadata-catalog/test_paging/"

    },

    {

      "rel": "first",

      "href": "https://localhost/ords/apexdev/test_paging/?limit=2"

    },

    {

      "rel": "next",

      "href": "https://localhost/ords/apexdev/test_paging/?offset=2&limit=2"

    }

  ]

}

% 


リバース・プロキシの有無に関わらず、リクエストの送信元が設定したHostヘッダーの値がORDSにまで渡っていれば、ORDSおよびAPEXはそれを送信元と認識するようです。

curl -H "Host: example.com" -k "https://localhost:9443/ords/apexdev/debug/print-http-headers"

% curl -H "Host: example.com" -k "https://localhost:9443/ords/apexdev/debug/print-http-headers"

REMOTE_IDENT = <br />

REMOTE_USER = WKSP_APEXDEV<br />

Host = example.com:9443<br />

X-Forwarded-Host = example.com:9443<br />

X-Real-IP = 10.89.0.5<br />

X-Forwarded-For = 10.89.0.5<br />

X-Forwarded-Proto = https<br />

Connection = close<br />

User-Agent = curl/8.7.1<br />

Accept = */*<br />

ORDS_VERSION = 25.3.1.r2891312<br />

APEX_LISTENER_VERSION = 25.3.1.r2891312<br />

DAD_NAME = <br />

DOC_ACCESS_PATH = <br />

DOCUMENT_TABLE = <br />

GATEWAY_IVERSION = 3<br />

GATEWAY_INTERFACE = CGI/1.1<br />

HTTP_ACCEPT = */*<br />

HTTP_ACCEPT_ENCODING = <br />

HTTP_ACCEPT_LANGUAGE = <br />

HTTP_ACCEPT_CHARSET = <br />

HTTP_IF_MODIFIED_SINCE = <br />

HTTP_IF_NONE_MATCH = <br />

HTTP_HOST = example.com<br />

HTTP_ORACLE_ECID = <br />

HTTP_PORT = 443<br />

HTTP_REFERER = <br />

HTTP_USER_AGENT = curl/8.7.1<br />

PATH_ALIAS = <br />

PATH_INFO = /print-http-headers<br />

PLSQL_GATEWAY = WebDb<br />

QUERY_STRING = <br />

REMOTE_ADDR = [0:0:0:0:0:0:0:1]<br />

REQUEST_CHARSET = AL32UTF8<br />

REQUEST_CONTEXT_PATH = /ords/apexdev<br />

REQUEST_CONTEXT_ROOT = /ords<br />

REQUEST_IANA_CHARSET = UTF-8<br />

REQUEST_METHOD = GET<br />

REQUEST_PROTOCOL = https<br />

REQUEST_SCHEME = https<br />

SCRIPT_NAME = /ords/apexdev/debug<br />

SCRIPT_PREFIX = <br />

SERVER_NAME = example.com<br />

SERVER_PORT = 443<br />

SERVER_PROTOCOL = HTTP/1.1<br />

SERVER_SOFTWARE = Mod-Apex<br />

WEB_AUTHENT_PREFIX = <br />

X-APEX-METHOD = GET<br />

X-APEX-BASE = https://example.com/ords/apexdev/<br />

X-APEX-PATH = debug/print-http-headers<br />

X-APEX-REMOTE-ADDRESS = [0:0:0:0:0:0:0:1]<br />

X-APEX-CHARSET = UTF-8<br />

HTTP_COOKIE = <br />

ynakakoshi@Ns-Macbook apex-podman-setup % 


リバース・プロキシ(ロード・バランサも同様)でHTTPSからHTTPへのプロトコル変換を行うように構成する場合、HTTPSのリクエストは443番に固定されるという制限があるようです。

通常はHTTPSのポートを443以外で運用しないので実害は無いと思いますが、テスト環境を作る際に443以外のポート番号を使いたい場合などは注意が必要です。

2025年11月10日月曜日

Stamen Map TilesをOracle APEXのマップ・リージョンの背景マップにする

米国San FranciscoのStamen Design社が開発したStamen Map Tilesを、Oracle APEXのマップ・リージョンの背景マップにしてみます。Stamen Map Tilesは現在Stadia Maps社のプラットフォームより配布されているため、Stamen Map Tilesを背景マップとして使用するには、Stadia Maps社にユーザー登録する必要があります。非商用用途に限定したFreeプランが提供されています。

Stadia Maps社から配布されているStamen Map Tilesを使用するにあたって、以下のサイトに記載されているFAQを参照します。

Stamen Design × Stadia Maps
https://stadiamaps.com/stamen/

設定については以下のページで説明されています。

Migration Guide for Stamen Map Tile Users
https://docs.stadiamaps.com/guides/migrating-from-stamen-map-tiles/

作成したAPEXアプリケーションのマップは、以下のように表示されます。マップ上に表示しているのは東京都オープンデータカタログサイトにあるTOKYO WALKING MAPより、水元・柴又エリア~寅さんの柴又帝釈天と水元公園へのみち~としてダウンロードできるKMLファイルを、データベースにロードして表示しています。


macOS上のpodmanで実行しているOracle APEXの環境で作業します。構築手順については、こちらの記事「podmanを使ってOracle Database FreeとOracle REST Data Servicesをコンテナとして実行する」で紹介しています。

以下より、APEXアプリケーションの作成手順を紹介します。

水元・柴又エリア~寅さんの柴又帝釈天と水元公園へのみち~を開き、ZIPファイルをダウンロードします。


ファイルは以下の名前でダウンロードされます。

131229_Mizumoto_and_Shibamata_Area_-_Tora-sans_Road_to_Shibamata_Taishakuten_and_Mizumoto_Park.zip

これはKMLファイルをZIP圧縮したファイルなので、KMZファイルとして扱うことができます。ファイル名をwalking.kmzに変更します。

以降の作業では、上記のファイルをwalking.kmzとして扱います。

GDALを使用してwalking.kmzをOracle Databaseにロードします。GDALを組み込んだコンテナの作成手順については、こちらの記事「GDALのogr2ogrを使ってShapefileをOracle DatabaseのSDO_GEOMETRY列にロードする」を参照してください。今回の作業を行なうにあたって、LIBKMLのドライバの組み込むようにDockerfileを更新しています。

GDALを組み込んだコンテナを実行します。

podman run --rm -it -v $PWD:/home/oracle gdal

% podman run --rm -it -v $PWD:/home/oracle gdal

bash-5.1$


Oracle Databaseのドライバが参照されるように、環境変数を設定します。
export LANG=ja_JP.utf8
export NLS_LANG=American_America.AL32UTF8
export PATH=/usr/local/bin:$PATH
export ORACLE_HOME=/usr/lib/oracle/23/client64
export LD_LIBRARY_PATH=${ORACLE_HOME}/lib:/usr/local/lib64:${LD_LIBRARY_PATH}

bash-5.1$ export LANG=ja_JP.utf8

export NLS_LANG=American_America.AL32UTF8

export PATH=/usr/local/bin:$PATH

export ORACLE_HOME=/usr/lib/oracle/23/client64

export LD_LIBRARY_PATH=${ORACLE_HOME}/lib:/usr/local/lib64:${LD_LIBRARY_PATH}

bash-5.1$ 


以下のogrinfoコマンドを実行し、KMZファイルに含まれるフォルダを一覧します。

ogrinfo -ro walking.kmz

今回、データベースにロードするのは無題のレイヤです。

bash-5.1$ ogrinfo -ro walking.kmz 

INFO: Open of `walking.kmz'

      using driver `LIBKML' successful.

1: 無題のレイヤ

2: 都立公園/Tokyo Metropolitan Parks and Gardens

3: 海上公園/Marine Parks

bash-5.1$ 


以下のogr2ogrコマンドを実行し、KMZファイルに含まれる散歩ルートをデータベースにロードします。
ogr2ogr -f OCI \
"OCI:wksp_apexdev/<パスワード>@host.containers.internal/freepdb1" \
./walking.kmz "無題のレイヤ" \
-nln TOKYO_WALKING_ROUTES \
-lco GEOMETRY_NAME=GEOM -lco SRID=4326 -nlt PROMOTE_TO_MULTI

bash-5.1$ ogr2ogr -f OCI \

"OCI:wksp_apexdev/********@host.containers.internal/freepdb1" \

./walking.kmz "無題のレイヤ" \

-nln TOKYO_WALKING_ROUTES \

-lco GEOMETRY_NAME=GEOM -lco SRID=4326 -nlt PROMOTE_TO_MULTI

bash-5.1$ 


エラーが発生しなければ、KMZ(KML)フォーマットで記載された散歩ルートはデータベースにロードされています。

APEXアプリケーションを作成し、マップ・リージョンで散歩ルートを表示します。

最初にSQLコマンドより、表TOKYO_WALKING_ROUTESにロードされたデータを確認します。以下のSELECT文を実行します。

select * from tokyo_walking_routes


データを確認すると、以下の3つの特殊な"Name"のデータが見つかります。これ以外は、観光地です。また、コース/Routeはポイントではなく行(LineString)です。

スタート/Start,ゴール/Goal,コース/Route

これらは、レイヤを分けて表示することにします。

APEXアプリケーションを作成します。

アプリケーション作成ウィザードを開き、デフォルトで作成されているホーム・ページを削除して、代わりにマップのページを追加します。

アプリケーションの名前TOKYO WALKING MAPとします。


マップのページ名散歩にはogr2ogrコマンドで作成したTOKYO_WALKING_ROUTESを選択します。ポイントを選択し、ジオメトリ列GEOMツールチップ列Nameを指定します。


以上でアプリケーションを作成します。

ページ・デザイナで作成されたアプリケーションのホーム・ページを開きます。


作成済みのレイヤ散歩から、列Nameの値がスタート/Startゴール/Goalコース/Routeのデータを除きます。これらは別のレイヤとして表示します。

ソースWHERE句に以下を記述します。

"Name" not in ('スタート/Start','ゴール/Goal','コース/Route')

情報ウィンドウタイトル列Name本体列descriptionを設定します。


レイヤ散歩を重複させ、スタート位置を表示するレイヤに変更します。

名前スタートソースWHERE句に以下を設定します。

"Name" = 'スタート/Start'

外観塗りつぶしの色を赤(#ff0000)にします。


ゴールを表示するレイヤを作成します。

名前ゴールソースWHERE句に以下を設定します。

"Name" = 'ゴール/Goal'

外観塗りつぶしの色を緑(#008000)にします。


コースを表示するレイヤを作成します。

名前コースレイヤー・タイプにします。ソースWHERE句に以下を設定します。

"Name" = 'コース/Route'

外観のストローク・スタイルに点線を選択し、ストロークの色を黒(#101010)にします。


以上で、水元・柴又エリアの散歩コースがマップ上に表示されるようになりました。


次にマップ背景を変更します。

共有コンポーネントマップ背景を開きます。


作成を開始します。


Standia Mapsにユーザー登録をして、APIキーを生成済みとします。

名前Stamen Terrainとし、タイプラスターXYZタイル・レイヤーを選択します。APIキーにStandia Mapsで取得したAPIキーを設定し、ラスター・タイル(XYZ) URLとして以下を記述します。

https://tiles.stadiamaps.com/tiles/stamen_terrain/{z}/{x}/{y}.png?api_key={api_key}

以上でマップ背景の作成をクリックします。


マップ背景が作成されます。属性に以下のコピーライト情報を記述します。
&copy; <a href="https://stadiamaps.com/" target="_blank">Stadia Maps</a> <a href="https://stamen.com/" target="_blank">&copy; Stamen Design</a>
&copy; <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a>
&copy; <a href="https://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>
以上で変更の適用をクリックします。


以上でマップ背景が作成されました。


マップ・リージョンの属性を開き、マップ背景共有コンポーネントを選択します。標準として先ほど作成したマップ背景Stamen Terrainを設定します。


以上でAPEXアプリケーションは完成です。アプリケーションを実行すると、以下のマップが表示されます。


今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/sample-kml-and-stamen-map-tiles.zip

Oracle APEXのアプリケーション作成の参考になれば幸いです。