Python buffer objects can have negative strides.
diff --git a/docs/advanced/pycpp/numpy.rst b/docs/advanced/pycpp/numpy.rst
index 6bcc467..42f376c 100644
--- a/docs/advanced/pycpp/numpy.rst
+++ b/docs/advanced/pycpp/numpy.rst
@@ -41,8 +41,8 @@
                 py::format_descriptor<float>::format(), /* Python struct-style format descriptor */
                 2,                                      /* Number of dimensions */
                 { m.rows(), m.cols() },                 /* Buffer dimensions */
-                { sizeof(float) * m.rows(),             /* Strides (in bytes) for each index */
-                  sizeof(float) }
+                { (ssize_t)( sizeof(float) * m.rows() ),/* Strides (in bytes) for each index */
+                  (ssize_t)( sizeof(float) ) }
             );
         });
 
@@ -61,7 +61,7 @@
         std::string format;
         int ndim;
         std::vector<size_t> shape;
-        std::vector<size_t> strides;
+        std::vector<ssize_t> strides;
     };
 
 To create a C++ function that can take a Python buffer object as an argument,
@@ -121,8 +121,8 @@
             { (size_t) m.rows(),
               (size_t) m.cols() },
             /* Strides (in bytes) for each index */
-            { sizeof(Scalar) * (rowMajor ? m.cols() : 1),
-              sizeof(Scalar) * (rowMajor ? 1 : m.rows()) }
+            { (ssize_t)( sizeof(Scalar) * (rowMajor ? m.cols() : 1) ),
+              (ssize_t)( sizeof(Scalar) * (rowMajor ? 1 : m.rows()) ) }
         );
      })
 
diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h
index ba9402b..f86df9c 100644
--- a/include/pybind11/numpy.h
+++ b/include/pybind11/numpy.h
@@ -265,14 +265,14 @@
     const unsigned char *data_;
     // Storing the shape & strides in local variables (i.e. these arrays) allows the compiler to
     // make large performance gains on big, nested loops, but requires compile-time dimensions
-    conditional_t<Dynamic, const size_t *, std::array<size_t, (size_t) Dims>>
-        shape_, strides_;
+    conditional_t<Dynamic, const size_t *, std::array<size_t, (size_t) Dims>> shape_;
+    conditional_t<Dynamic, const ssize_t *, std::array<ssize_t, (size_t) Dims>> strides_;
     const size_t dims_;
 
     friend class pybind11::array;
     // Constructor for compile-time dimensions:
     template <bool Dyn = Dynamic>
-    unchecked_reference(const void *data, const size_t *shape, const size_t *strides, enable_if_t<!Dyn, size_t>)
+    unchecked_reference(const void *data, const size_t *shape, const ssize_t *strides, enable_if_t<!Dyn, size_t>)
     : data_{reinterpret_cast<const unsigned char *>(data)}, dims_{Dims} {
         for (size_t i = 0; i < dims_; i++) {
             shape_[i] = shape[i];
@@ -281,7 +281,7 @@
     }
     // Constructor for runtime dimensions:
     template <bool Dyn = Dynamic>
-    unchecked_reference(const void *data, const size_t *shape, const size_t *strides, enable_if_t<Dyn, size_t> dims)
+    unchecked_reference(const void *data, const size_t *shape, const ssize_t *strides, enable_if_t<Dyn, size_t> dims)
     : data_{reinterpret_cast<const unsigned char *>(data)}, shape_{shape}, strides_{strides}, dims_{dims} {}
 
 public:
@@ -573,12 +573,12 @@
     }
 
     /// Strides of the array
-    const size_t* strides() const {
-        return reinterpret_cast<const size_t *>(detail::array_proxy(m_ptr)->strides);
+    const ssize_t* strides() const {
+        return reinterpret_cast<const ssize_t *>(detail::array_proxy(m_ptr)->strides);
     }
 
     /// Stride along a given axis
-    size_t strides(size_t dim) const {
+    ssize_t strides(size_t dim) const {
         if (dim >= ndim())
             fail_dim_check(dim, "invalid axis");
         return strides()[dim];
@@ -702,9 +702,9 @@
             throw std::domain_error("array is not writeable");
     }
 
-    static std::vector<Py_intptr_t> default_strides(const std::vector<Py_intptr_t>& shape, size_t itemsize) {
+    static std::vector<ssize_t> default_strides(const std::vector<size_t>& shape, size_t itemsize) {
         auto ndim = shape.size();
-        std::vector<Py_intptr_t> strides(ndim);
+        std::vector<ssize_t> strides(ndim);
         if (ndim) {
             std::fill(strides.begin(), strides.end(), itemsize);
             for (size_t i = 0; i < ndim - 1; i++)
@@ -1133,7 +1133,7 @@
 
 class common_iterator {
 public:
-    using container_type = std::vector<size_t>;
+    using container_type = std::vector<ssize_t>;
     using value_type = container_type::value_type;
     using size_type = container_type::size_type;
 
@@ -1175,7 +1175,7 @@
         for (size_t i = 0; i < shape.size(); ++i)
             m_shape[i] = static_cast<container_type::value_type>(shape[i]);
 
-        container_type strides(shape.size());
+        std::vector<ssize_t> strides(shape.size());
         for (size_t i = 0; i < N; ++i)
             init_common_iterator(buffers[i], shape, m_common_iterator[i], strides);
     }
@@ -1203,7 +1203,7 @@
 
     void init_common_iterator(const buffer_info &buffer,
                               const std::vector<size_t> &shape,
-                              common_iter &iterator, container_type &strides) {
+                              common_iter &iterator, std::vector<ssize_t> &strides) {
         auto buffer_shape_iter = buffer.shape.rbegin();
         auto buffer_strides_iter = buffer.strides.rbegin();
         auto shape_iter = shape.rbegin();
@@ -1211,7 +1211,7 @@
 
         while (buffer_shape_iter != buffer.shape.rend()) {
             if (*shape_iter == *buffer_shape_iter)
-                *strides_iter = static_cast<size_t>(*buffer_strides_iter);
+                *strides_iter = *buffer_strides_iter;
             else
                 *strides_iter = 0;
 
@@ -1283,10 +1283,11 @@
 
         // Check for C contiguity (but only if previous inputs were also C contiguous)
         if (trivial_broadcast_c) {
-            size_t expect_stride = buffers[i].itemsize;
+            ssize_t expect_stride = static_cast<ssize_t>(buffers[i].itemsize);
             auto end = buffers[i].shape.crend();
-            for (auto shape_iter = buffers[i].shape.crbegin(), stride_iter = buffers[i].strides.crbegin();
-                    trivial_broadcast_c && shape_iter != end; ++shape_iter, ++stride_iter) {
+            auto shape_iter = buffers[i].shape.crbegin();
+            auto stride_iter = buffers[i].strides.crbegin();
+            for (; trivial_broadcast_c && shape_iter != end; ++shape_iter, ++stride_iter) {
                 if (expect_stride == *stride_iter)
                     expect_stride *= *shape_iter;
                 else
@@ -1296,10 +1297,11 @@
 
         // Check for Fortran contiguity (if previous inputs were also F contiguous)
         if (trivial_broadcast_f) {
-            size_t expect_stride = buffers[i].itemsize;
+            ssize_t expect_stride = static_cast<ssize_t>(buffers[i].itemsize);
             auto end = buffers[i].shape.cend();
-            for (auto shape_iter = buffers[i].shape.cbegin(), stride_iter = buffers[i].strides.cbegin();
-                    trivial_broadcast_f && shape_iter != end; ++shape_iter, ++stride_iter) {
+            auto shape_iter = buffers[i].shape.cbegin();
+            auto stride_iter = buffers[i].strides.cbegin();
+            for (; trivial_broadcast_f && shape_iter != end; ++shape_iter, ++stride_iter) {
                 if (expect_stride == *stride_iter)
                     expect_stride *= *shape_iter;
                 else
@@ -1336,20 +1338,20 @@
         auto trivial = broadcast(buffers, ndim, shape);
 
         size_t size = 1;
-        std::vector<size_t> strides(ndim);
+        std::vector<ssize_t> strides(ndim);
         if (ndim > 0) {
             if (trivial == broadcast_trivial::f_trivial) {
-                strides[0] = sizeof(Return);
+                strides[0] = static_cast<ssize_t>(sizeof(Return));
                 for (size_t i = 1; i < ndim; ++i) {
-                    strides[i] = strides[i - 1] * shape[i - 1];
+                    strides[i] = strides[i - 1] * static_cast<ssize_t>(shape[i - 1]);
                     size *= shape[i - 1];
                 }
                 size *= shape[ndim - 1];
             }
             else {
-                strides[ndim-1] = sizeof(Return);
+                strides[ndim-1] = static_cast<ssize_t>(sizeof(Return));
                 for (size_t i = ndim - 1; i > 0; --i) {
-                    strides[i - 1] = strides[i] * shape[i];
+                    strides[i - 1] = strides[i] * static_cast<ssize_t>(shape[i]);
                     size *= shape[i];
                 }
                 size *= shape[0];
diff --git a/tests/test_buffers.cpp b/tests/test_buffers.cpp
index 057250d..d533bd3 100644
--- a/tests/test_buffers.cpp
+++ b/tests/test_buffers.cpp
@@ -109,8 +109,8 @@
                 py::format_descriptor<float>::format(), /* Python struct-style format descriptor */
                 2,                                      /* Number of dimensions */
                 { m.rows(), m.cols() },                 /* Buffer dimensions */
-                { sizeof(float) * m.rows(),             /* Strides (in bytes) for each index */
-                  sizeof(float) }
+                { static_cast<ssize_t>(sizeof(float) * m.rows()), /* Strides (in bytes) for each index */
+                  static_cast<ssize_t>(sizeof(float)) }
             );
         })
         ;
diff --git a/tests/test_numpy_array.cpp b/tests/test_numpy_array.cpp
index 85c185f..e431e09 100644
--- a/tests/test_numpy_array.cpp
+++ b/tests/test_numpy_array.cpp
@@ -13,6 +13,7 @@
 #include <pybind11/stl.h>
 
 #include <cstdint>
+#include <vector>
 
 using arr = py::array;
 using arr_t = py::array_t<uint16_t, 0>;
@@ -294,4 +295,4 @@
         std::fill(a.mutable_data(), a.mutable_data() + a.size(), 42.);
         return a;
     });
-});
\ No newline at end of file
+});
diff --git a/tests/test_numpy_array.py b/tests/test_numpy_array.py
index 10af748..90f9b80 100644
--- a/tests/test_numpy_array.py
+++ b/tests/test_numpy_array.py
@@ -203,6 +203,10 @@
     a2 = wrap(a1d)
     assert_references(a1d, a2, a1)
 
+    a1m = a1[::-1, ::-1, ::-1]
+    a2 = wrap(a1m)
+    assert_references(a1m, a2, a1)
+
 
 def test_numpy_view(capture):
     from pybind11_tests.array import ArrayClass
diff --git a/tests/test_numpy_dtypes.cpp b/tests/test_numpy_dtypes.cpp
index 1f6c857..4fc797c 100644
--- a/tests/test_numpy_dtypes.cpp
+++ b/tests/test_numpy_dtypes.cpp
@@ -226,7 +226,7 @@
 
     std::vector<int32_t> data { 1, 2, 3, 4, 5, 6 };
     std::vector<size_t> shape { 3, 2 };
-    std::vector<size_t> strides { 8, 4 };
+    std::vector<ssize_t> strides { 8, 4 };
 
     auto ptr = data.data();
     auto vptr = (void *) ptr;