diff --git a/sys/include/net/nanocoap.h b/sys/include/net/nanocoap.h
index 3ea265d4ff0c0ca05c447ab21da04de999644b0f..a8b069481765ee0f114e50864b9d7ba6433f231d 100644
--- a/sys/include/net/nanocoap.h
+++ b/sys/include/net/nanocoap.h
@@ -47,10 +47,15 @@ extern "C" {
  * @name    Nanocoap specific maximum values
  * @{
  */
-#define NANOCOAP_URL_MAX        (64)
-#define NANOCOAP_QS_MAX         (64)
+#define NANOCOAP_NOPTS_MAX      (16)
+#define NANOCOAP_URI_MAX        (64)
 /** @} */
 
+#ifdef MODULE_GCOAP
+#define NANOCOAP_URL_MAX        NANOCOAP_URI_MAX
+#define NANOCOAP_QS_MAX         (64)
+#endif
+
 /**
  * @name    CoAP option numbers
  * @{
@@ -220,14 +225,26 @@ typedef struct __attribute__((packed)) {
  * @brief   CoAP option array entry
  */
 typedef struct {
-    coap_hdr_t *hdr;                /**< pointer to raw packet              */
-    uint8_t url[NANOCOAP_URL_MAX];  /**< parsed request URL                 */
-    uint8_t qs[NANOCOAP_QS_MAX];    /**< parsed query string                */
-    uint8_t *token;                 /**< pointer to token                   */
-    uint8_t *payload;               /**< pointer to payload                 */
-    unsigned payload_len;           /**< length of payload                  */
-    uint16_t content_type;          /**< content type                       */
-    uint32_t observe_value;         /**< observe value                      */
+    uint16_t opt_num;           /**< full CoAP option number    */
+    uint16_t offset;            /**< offset in packet           */
+} coap_optpos_t;
+
+/**
+ * @brief   CoAP PDU parsing context structure
+ */
+typedef struct {
+    coap_hdr_t *hdr;                            /**< pointer to raw packet   */
+    uint8_t *token;                             /**< pointer to token        */
+    uint8_t *payload;                           /**< pointer to payload      */
+    uint16_t payload_len;                       /**< length of payload       */
+    uint16_t options_len;                       /**< length of options array */
+    coap_optpos_t options[NANOCOAP_NOPTS_MAX];  /**< option offset array     */
+#ifdef MODULE_GCOAP
+    uint8_t url[NANOCOAP_URI_MAX];              /**< parsed request URL      */
+    uint8_t qs[NANOCOAP_QS_MAX];                /**< parsed query string     */
+    uint16_t content_type;                      /**< content type            */
+    uint32_t observe_value;                     /**< observe value           */
+#endif
 } coap_pkt_t;
 
 /**
@@ -392,6 +409,33 @@ size_t coap_put_option_ct(uint8_t *buf, uint16_t lastonum, uint16_t content_type
  */
 size_t coap_put_option_uri(uint8_t *buf, uint16_t lastonum, const char *uri, uint16_t optnum);
 
+
+/**
+ * @brief    Get content type from packet
+ *
+ * @param[in]   pkt     packet to work on
+ *
+ * @returns     the packet's content type value if included,
+ *              COAP_FORMAT_NONE otherwise
+ */
+unsigned coap_get_content_type(coap_pkt_t *pkt);
+
+/**
+ * @brief    Get the packet's request URI
+ *
+ * This function decodes the pkt's URI option into a "/"-seperated and
+ * NULL-terminated string.
+ *
+ * Caller must ensure @p target can hold at least NANOCOAP_URI_MAX bytes!
+ *
+ * @param[in]   pkt     pkt to work on
+ * @param[out]  target  buffer for target URI
+ *
+ * @returns     -ENOSPC     if URI option is larger than NANOCOAP_URI_MAX
+ * @returns     nr of bytes written to @p target (including '\0')
+ */
+int coap_get_uri(coap_pkt_t *pkt, uint8_t *target);
+
 /**
  * @brief   Get the CoAP version number
  *
@@ -556,6 +600,7 @@ static inline unsigned coap_method2flag(unsigned code)
     return (1 << (code - 1));
 }
 
+#if defined(MODULE_GCOAP) || defined(DOXYGEN)
 /**
  * @brief   Identifies a packet containing an observe option
  *
@@ -590,6 +635,7 @@ static inline uint32_t coap_get_observe(coap_pkt_t *pkt)
 {
     return pkt->observe_value;
 }
+#endif
 
 /**
  * @brief   Reference to the default .well-known/core handler defined by the
diff --git a/sys/net/application_layer/nanocoap/nanocoap.c b/sys/net/application_layer/nanocoap/nanocoap.c
index 27e810524f880c3feb5e3e7e09780b3bb3d61ec7..0f04395e3d1bcde9c3d4db24c26331c032185adc 100644
--- a/sys/net/application_layer/nanocoap/nanocoap.c
+++ b/sys/net/application_layer/nanocoap/nanocoap.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016-17 Kaspar Schleiser <kaspar@schleiser.de>
+ * Copyright (C) 2016-18 Kaspar Schleiser <kaspar@schleiser.de>
  *
  * This file is subject to the terms and conditions of the GNU Lesser
  * General Public License v2.1. See the file LICENSE in the top level
@@ -29,7 +29,7 @@
 #include "debug.h"
 
 static int _decode_value(unsigned val, uint8_t **pkt_pos_ptr, uint8_t *pkt_end);
-static uint32_t _decode_uint(uint8_t *pkt_pos, unsigned nbytes);
+int coap_get_option_uint(coap_pkt_t *pkt, unsigned opt_num, uint32_t *target);
 
 /* http://tools.ietf.org/html/rfc7252#section-3
  *  0                   1                   2                   3
@@ -46,17 +46,14 @@ static uint32_t _decode_uint(uint8_t *pkt_pos, unsigned nbytes);
  */
 int coap_parse(coap_pkt_t *pkt, uint8_t *buf, size_t len)
 {
-    uint8_t *urlpos = pkt->url;
     coap_hdr_t *hdr = (coap_hdr_t *)buf;
-
     pkt->hdr = hdr;
 
     uint8_t *pkt_pos = hdr->data;
     uint8_t *pkt_end = buf + len;
 
-    memset(pkt->url, '\0', NANOCOAP_URL_MAX);
+    pkt->payload = NULL;
     pkt->payload_len = 0;
-    pkt->observe_value = UINT32_MAX;
 
     /* token value (tkl bytes) */
     if (coap_get_token_len(pkt)) {
@@ -67,9 +64,13 @@ int coap_parse(coap_pkt_t *pkt, uint8_t *buf, size_t len)
         pkt->token = NULL;
     }
 
+    coap_optpos_t *optpos = pkt->options;
+    unsigned option_count = 0;
+    unsigned option_nr = 0;
+
     /* parse options */
-    int option_nr = 0;
     while (pkt_pos != pkt_end) {
+        uint8_t *option_start = pkt_pos;
         uint8_t option_byte = *pkt_pos++;
         if (option_byte == 0xff) {
             pkt->payload = pkt_pos;
@@ -89,58 +90,181 @@ int coap_parse(coap_pkt_t *pkt, uint8_t *buf, size_t len)
                 return -EBADMSG;
             }
             option_nr += option_delta;
-            DEBUG("option nr=%i len=%i\n", option_nr, option_len);
-
-            switch (option_nr) {
-                case COAP_OPT_URI_HOST:
-                    DEBUG("nanocoap: ignoring Uri-Host option!\n");
-                    break;
-                case COAP_OPT_URI_PATH:
-                    *urlpos++ = '/';
-                    memcpy(urlpos, pkt_pos, option_len);
-                    urlpos += option_len;
-                    break;
-                case COAP_OPT_CONTENT_FORMAT:
-                    if (option_len == 0) {
-                        pkt->content_type = 0;
-                    }
-                    else if (option_len == 1) {
-                        pkt->content_type = *pkt_pos;
-                    }
-                    else if (option_len == 2) {
-                        memcpy(&pkt->content_type, pkt_pos, 2);
-                        pkt->content_type = ntohs(pkt->content_type);
-                    }
-                    break;
-                case COAP_OPT_OBSERVE:
-                    if (option_len < 4) {
-                        pkt->observe_value = _decode_uint(pkt_pos, option_len);
-                    }
-                    else {
-                        DEBUG("nanocoap: discarding packet with invalid option length.\n");
-                        return -EBADMSG;
-                    }
-                    break;
-                default:
-                    DEBUG("nanocoap: unhandled option nr=%i len=%i critical=%u\n", option_nr, option_len, option_nr & 1);
-                    if (option_nr & 1) {
-                        DEBUG("nanocoap: discarding packet with unknown critical option.\n");
-                        return -EBADMSG;
-                    }
+            DEBUG("option count=%u nr=%u len=%i\n", option_count, option_nr, option_len);
+
+            if (option_delta) {
+                optpos->opt_num = option_nr;
+                optpos->offset = (uintptr_t)option_start - (uintptr_t)hdr;
+                DEBUG("optpos option_nr=%u %u\n", (unsigned)option_nr, (unsigned)optpos->offset);
+                optpos++;
+                option_count++;
             }
 
             pkt_pos += option_len;
+
+            if (pkt_pos > (buf + len)) {
+                DEBUG("nanocoap: bad pkt\n");
+                return -EBADMSG;
+            }
         }
     }
 
-    DEBUG("coap pkt parsed. code=%u detail=%u payload_len=%u, 0x%02x\n",
+    pkt->options_len = option_count;
+    if (!pkt->payload) {
+        pkt->payload = pkt_pos;
+    }
+
+#ifdef MODULE_GCOAP
+    coap_get_uri(pkt, pkt->url);
+    pkt->content_type = coap_get_content_type(pkt);
+
+    if (coap_get_option_uint(pkt, COAP_OPT_OBSERVE, &pkt->observe_value) != 0) {
+        pkt->observe_value = UINT32_MAX;
+    }
+#endif
+
+    DEBUG("coap pkt parsed. code=%u detail=%u payload_len=%u, nopts=%u, 0x%02x\n",
           coap_get_code_class(pkt),
           coap_get_code_detail(pkt),
-          pkt->payload_len, hdr->code);
+          pkt->payload_len, option_count, hdr->code);
 
     return 0;
 }
 
+uint8_t *coap_find_option(coap_pkt_t *pkt, unsigned opt_num)
+{
+    coap_optpos_t *optpos = pkt->options;
+    unsigned opt_count = pkt->options_len;
+
+    while (opt_count--) {
+        if (optpos->opt_num == opt_num) {
+            return (uint8_t*)pkt->hdr + optpos->offset;
+        }
+        optpos++;
+    }
+    return NULL;
+}
+
+static uint8_t *_parse_option(coap_pkt_t *pkt, uint8_t *pkt_pos, uint16_t *delta, int *opt_len)
+{
+    uint8_t *hdr_end = pkt->payload;
+
+    if (pkt_pos == hdr_end) {
+        return NULL;
+    }
+
+    uint8_t option_byte = *pkt_pos++;
+
+    *delta = _decode_value(option_byte >> 4, &pkt_pos, hdr_end);
+    *opt_len = _decode_value(option_byte & 0xf, &pkt_pos, hdr_end);
+
+    return pkt_pos;
+}
+
+static uint32_t _decode_uint(uint8_t *pkt_pos, unsigned nbytes)
+{
+    assert(nbytes <= 4);
+
+    uint32_t res = 0;
+    if (nbytes) {
+        memcpy(((uint8_t *)&res) + (4 - nbytes), pkt_pos, nbytes);
+    }
+    return ntohl(res);
+}
+
+int coap_get_option_uint(coap_pkt_t *pkt, unsigned opt_num, uint32_t *target)
+{
+    assert(target);
+
+    uint8_t *opt_pos = coap_find_option(pkt, opt_num);
+    if (opt_pos) {
+        uint16_t delta;
+        int option_len = 0;
+        uint8_t *pkt_pos = _parse_option(pkt, opt_pos, &delta, &option_len);
+        if (option_len >= 0) {
+            if (option_len > 4) {
+                DEBUG("nanocoap: uint option with len > 4 (unsupported).\n");
+                return -ENOSPC;
+            }
+            *target = _decode_uint(pkt_pos, option_len);
+            return 0;
+        }
+        else {
+            DEBUG("nanocoap: discarding packet with invalid option length.\n");
+            return -EBADMSG;
+        }
+    }
+    return -1;
+}
+
+uint8_t *coap_iterate_option(coap_pkt_t *pkt, uint8_t **optpos, int *opt_len, int first)
+{
+    uint8_t *data_start;
+
+    uint16_t delta = 0;
+    data_start = _parse_option(pkt, *optpos, &delta, opt_len);
+    if (data_start && (first || !delta)) {
+        *optpos = data_start + *opt_len;
+        return data_start;
+    }
+    else {
+        *optpos = NULL;
+        return NULL;
+    }
+}
+
+unsigned coap_get_content_type(coap_pkt_t *pkt)
+{
+    uint8_t *opt_pos = coap_find_option(pkt, COAP_OPT_CONTENT_FORMAT);
+    unsigned content_type = COAP_FORMAT_NONE;
+    if (opt_pos) {
+        uint16_t delta;
+        int option_len = 0;
+        uint8_t *pkt_pos = _parse_option(pkt, opt_pos, &delta, &option_len);
+
+        if (option_len == 0) {
+            content_type = 0;
+        } else if (option_len == 1) {
+            content_type = *pkt_pos;
+        } else if (option_len == 2) {
+            memcpy(&content_type, pkt_pos, 2);
+            content_type = ntohs(content_type);
+        }
+    }
+
+    return content_type;
+}
+
+int coap_get_uri(coap_pkt_t *pkt, uint8_t *target)
+{
+    uint8_t *opt_pos = coap_find_option(pkt, COAP_OPT_URI_PATH);
+    if (!opt_pos) {
+        *target++ = '/';
+        *target = '\0';
+        return 2;
+    }
+
+    unsigned left = NANOCOAP_URI_MAX - 1;
+    uint8_t *part_start = NULL;
+    do {
+        int opt_len;
+        part_start = coap_iterate_option(pkt, &opt_pos, &opt_len, part_start==NULL);
+        if (part_start) {
+            if (left < (unsigned)(opt_len + 1)) {
+                return -ENOSPC;
+            }
+            *target++ = '/';
+            memcpy(target, part_start, opt_len);
+            target += opt_len;
+            left -= (opt_len + 1);
+        }
+    } while(opt_pos);
+
+    *target = '\0';
+
+    return NANOCOAP_URI_MAX - left;
+}
+
 ssize_t coap_handle_req(coap_pkt_t *pkt, uint8_t *resp_buf, unsigned resp_buf_len)
 {
     if (coap_get_code_class(pkt) != COAP_REQ) {
@@ -154,13 +278,23 @@ ssize_t coap_handle_req(coap_pkt_t *pkt, uint8_t *resp_buf, unsigned resp_buf_le
 
     unsigned method_flag = coap_method2flag(coap_get_code_detail(pkt));
 
+#ifdef MODULE_GCOAP
+    uint8_t *uri = pkt->url;
+#else
+    uint8_t uri[NANOCOAP_URI_MAX];
+    if (coap_get_uri(pkt, uri) <= 0) {
+        return -EBADMSG;
+    }
+#endif
+    DEBUG("nanocoap: URI path: \"%s\"\n", uri);
+
     for (unsigned i = 0; i < coap_resources_numof; i++) {
         const coap_resource_t *resource = &coap_resources[i];
         if (!(resource->methods & method_flag)) {
             continue;
         }
 
-        int res = strcmp((char *)pkt->url, resource->path);
+        int res = strcmp((char *)uri, resource->path);
         if (res > 0) {
             continue;
         }
@@ -282,17 +416,6 @@ static int _decode_value(unsigned val, uint8_t **pkt_pos_ptr, uint8_t *pkt_end)
     return res;
 }
 
-static uint32_t _decode_uint(uint8_t *pkt_pos, unsigned nbytes)
-{
-    assert(nbytes <= 4);
-
-    uint32_t res = 0;
-    if (nbytes) {
-        memcpy(((uint8_t *)&res) + (4 - nbytes), pkt_pos, nbytes);
-    }
-    return ntohl(res);
-}
-
 static unsigned _put_delta_optlen(uint8_t *buf, unsigned offset, unsigned shift, unsigned val)
 {
     if (val < 13) {
@@ -408,3 +531,12 @@ ssize_t coap_well_known_core_default_handler(coap_pkt_t *pkt, uint8_t *buf, \
 
     return coap_build_reply(pkt, COAP_CODE_205, buf, len, payload_len);
 }
+
+unsigned coap_get_len(coap_pkt_t *pkt)
+{
+    unsigned pktlen = sizeof(coap_hdr_t) + coap_get_token_len(pkt);
+    if (pkt->payload) {
+        pktlen += pkt->payload_len + 1;
+    }
+    return pktlen;
+}
diff --git a/tests/unittests/tests-nanocoap/tests-nanocoap.c b/tests/unittests/tests-nanocoap/tests-nanocoap.c
index 8bad21e6a221a8c95166704e66f84f39977745c9..d5694344359259e8e34afd900cd5fad0bc2ccb7f 100644
--- a/tests/unittests/tests-nanocoap/tests-nanocoap.c
+++ b/tests/unittests/tests-nanocoap/tests-nanocoap.c
@@ -22,13 +22,14 @@
 #include "tests-nanocoap.h"
 
 /*
- * Validates encoded message ID byte order.
+ * Validates encoded message ID byte order and put/get URI option.
  */
-static void test_nanocoap__req_msgid(void)
+static void test_nanocoap__hdr(void)
 {
     uint8_t buf[128];
     uint16_t msgid = 0xABCD;
-    char path[] = "/test";
+    char path[] = "/test/abcd/efgh";
+    unsigned char path_tmp[64] = {0};
 
     uint8_t *pktpos = &buf[0];
     pktpos += coap_build_hdr((coap_hdr_t *)pktpos, COAP_REQ, NULL, 0, COAP_METHOD_GET, msgid);
@@ -38,12 +39,16 @@ static void test_nanocoap__req_msgid(void)
     coap_parse(&pkt, &buf[0], pktpos - &buf[0]);
 
     TEST_ASSERT_EQUAL_INT(msgid, coap_get_id(&pkt));
+
+    int res = coap_get_uri(&pkt, path_tmp);
+    TEST_ASSERT_EQUAL_INT(sizeof(path), res);
+    TEST_ASSERT_EQUAL_STRING((char *)path, (char *)path_tmp);
 }
 
 Test *tests_nanocoap_tests(void)
 {
     EMB_UNIT_TESTFIXTURES(fixtures) {
-        new_TestFixture(test_nanocoap__req_msgid),
+        new_TestFixture(test_nanocoap__hdr),
     };
 
     EMB_UNIT_TESTCALLER(nanocoap_tests, NULL, NULL, fixtures);